C++/コードギャラリー

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++


コードギャラリー[編集]

このコードギャラリーは、さまざまなC++の機能やパターン、ベストプラクティスを示すためのサンプルコード集です。

行列型[編集]

C++はユーザーが定義したクラスのメンバー演算子を定義することができます。

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <vector>

template <size_t Rows, size_t Cols, typename T> class Matrix {
private:
  std::vector<T> mat;

public:
  // コンストラクタ
  Matrix() : mat(Rows * Cols, static_cast<T>(0)) {}

  // ディメンジョンが一致するかチェック
  bool dimensionsMatch(const Matrix &other, const char *operation) const {
    if (Rows != other.getRows() || Cols != other.getCols()) {
      std::ostringstream errorMessage;
      errorMessage << "Matrix dimensions do not match for " << operation << ": "
                   << "(" << getRows() << "x" << Cols << " and "
                   << other.getRows() << "x" << other.getCols() << ")";
      throw std::invalid_argument(errorMessage.str());
    }
    return true;
  }

  // constexprメソッドで行数と列数を取得
  constexpr size_t getRows() const { return Rows; }
  constexpr size_t getCols() const { return Cols; }

  // 加算
  Matrix operator+(const Matrix &other) const {
    dimensionsMatch(other, __func__);
    Matrix result;
    for (size_t i = 0; i < Rows * Cols; ++i) {
      result.mat[i] = mat[i] + other.mat[i];
    }
    return result;
  }

  // 減算
  Matrix operator-(const Matrix &other) const {
    dimensionsMatch(other, __func__);
    Matrix result;
    for (size_t i = 0; i < Rows * Cols; ++i) {
      result.mat[i] = mat[i] - other.mat[i];
    }
    return result;
  }

  // 乗算
  Matrix operator*(const Matrix &other) const {
    if (Cols != other.getRows()) {
      throw std::invalid_argument(
          "Matrix dimensions do not match for multiplication");
    }

    Matrix result;
    for (size_t i = 0; i < Rows; ++i) {
      for (size_t j = 0; j < other.getCols(); ++j) {
        for (size_t k = 0; k < Cols; ++k) {
          result.mat[i * Cols + j] +=
              mat[i * Cols + k] * other.mat[k * other.getCols() + j];
        }
      }
    }
    return result;
  }

  // 除算
  Matrix operator/(const T &scalar) const {
    if (scalar == 0) {
      throw std::invalid_argument("Division by zero");
    }

    Matrix result;
    for (size_t i = 0; i < Rows * Cols; ++i) {
      result.mat[i] = mat[i] / scalar;
    }
    return result;
  }

  // 要素へのアクセス
  T &operator()(size_t i, size_t j) {
    if (i >= Rows || j >= Cols) {
      throw std::out_of_range("Matrix indices out of range");
    }
    return mat[i * Cols + j];
  }

  const T &operator()(size_t i, size_t j) const {
    if (i >= Rows || j >= Cols) {
      throw std::out_of_range("Matrix indices out of range");
    }
    return mat[i * Cols + j];
  }

  // 文字列表現
  std::string toString() const {
    std::ostringstream oss;
    for (size_t i = 0; i < Rows; ++i) {
      for (size_t j = 0; j < Cols; ++j) {
        oss << mat[i * Cols + j] << " ";
      }
      oss << std::endl;
    }
    return oss.str();
  }

  // ストリーム出力
  friend std::ostream &operator<<(std::ostream &os, const Matrix &matrix) {
    for (size_t i = 0; i < Rows; ++i) {
      for (size_t j = 0; j < Cols; ++j) {
        os << matrix.mat[i * Cols + j] << " ";
      }
      if (i != Rows - 1) {
        os << std::endl;
      }
    }
    return os;
  }
};

int main() {
  auto mat1 = Matrix<2, 2, int>();
  auto mat2 = Matrix<2, 2, int>();
  auto mat3 = Matrix<2, 3, int>();

  // auto mat = mat2 + mat3; // この行のコメントをはずすと
  // auto mat = mat2 + mat3;
  //            ~~~~ ^ ~~~~
  // mat.cpp:31:10: note: candidate function not viable: no known conversion from 'Matrix<[...], 3, [...]>' to 'const Matrix<[...], 2, [...]>' for 1st argument
  // Matrix operator+(const Matrix &other) const {
  //        ^
  // となります

  // 要素の設定
  mat1(0, 0) = 1;
  mat1(0, 1) = 2;
  mat1(1, 0) = 3;
  mat1(1, 1) = 4;

  mat2(0, 0) = 5;
  mat2(0, 1) = 6;
  mat2(1, 0) = 7;
  mat2(1, 1) = 8;

  // 加算
  std::cout << mat1 << std::endl
            << " +" << std::endl
            << mat2 << std::endl
            << " =" << std::endl
            << mat1 + mat2 << std::endl
            << std::endl;

  // 減算
  std::cout << mat1 << std::endl
            << " -" << std::endl
            << mat2 << std::endl
            << " =" << std::endl
            << mat1 - mat2 << std::endl
            << std::endl;

  // 乗算
  std::cout << mat1 << std::endl
            << " *" << std::endl
            << mat2 << std::endl
            << " =" << std::endl
            << mat1 * mat2 << std::endl
            << std::endl;

  // 除算
  std::cout << mat1 << std::endl
            << " / 2 =" << std::endl
            << mat1 / 2 << std::endl
            << std::endl;

  return 0;
}

このC++プログラムは、行列演算を行うためのテンプレートクラス Matrix を定義しています。以下に、プログラムの主な要素と機能を解説します。

  1. Matrix クラス:
    • Matrix クラスは、行列を表現するためのテンプレートクラスです。Rows および Cols は行列の行数と列数を示します。T は行列の要素の型を表します。
  2. コンストラクタ:
    • 行列を初期化するデフォルトコンストラクタが定義されています。全ての要素は初期値として 0 が設定されます。
  3. dimensionsMatch メソッド:
    • 行列の次元が一致するかどうかを確認するためのメソッドです。演算が実行される前に、行列の次元が一致していることを確認します。
  4. 四則演算のオーバーロード:
    • +, -, *, / 演算子がオーバーロードされています。これらの演算は、次元の一致を確認した上で、対応する要素ごとの操作を行います。
  5. operator() メソッド:
    • 行列の要素にアクセスするためのメソッドです。範囲外のインデックスへのアクセスをチェックし、要素への読み書きができます。
  6. toString メソッド:
    • 行列を文字列として表現するためのメソッドです。行ごとに要素をスペース区切りで表示し、改行で行を区切ります。
  7. operator<< 演算子のオーバーロード:
    • ストリーム出力演算子 << がオーバーロードされ、行列を標準出力に表示するためのメソッドです。
  8. main 関数:
    • main 関数では、Matrix クラスを使用して行列の加算、減算、乗算、除算を行います。また、要素へのアクセスや表示などが行われています。
  9. 注意事項:
    • Matrix クラスの演算は、行列の次元が一致していることを前提としています。行列の次元が異なる場合、例外がスローされます。
  10. コメント:
    • コメントによれば、auto mat = mat2 + mat3; の行をコメントアウトすると、コンパイルエラーが発生すると説明されています。これは、行列の次元が一致しないため、加算ができないことを示しています。

エラトステネスの篩[編集]

このC++のコードは、エラトステネスの篩を使用して指定された数以下の素数を見つけるプログラムです。以下にコードの各部分の解説を示します。

#include <iostream>
#include <vector>

int main() {
  const int n = 100;
  std::vector<bool> sieve(n + 1, true);

  // 初期化: 2以上の数は全てtrue
  for (int i = 2; i <= n; i++) {
    if (sieve[i]) {
      std::cout << i << " ";

      for (int j = 2 * i; j <= n; j += i) {
        sieve[j] = false;
      }
    }
  }

  return 0;
}

最大公約数と最小公倍数[編集]

#include <iostream>

// GCDの計算
int gcd2(int m, int n) {
  if (n == 0) {
    return m;
  } else {
    return gcd2(n, m % n);
  }
}

// 可変引数のGCDの計算
template<typename T, typename... Args>
T gcd(T first, Args... args) {
    return gcd2(first, gcd2(args...));
}

// LCMの計算
int lcm2(int m, int n) { return m * n / gcd2(m, n); }

// 可変引数のLCMの計算
template <typename T, typename... Args>
T lcm(T first, Args... args) {
    return lcm2(first, lcm2(args...));
}

int main() {
  // GCDとLCMの計算
  std::cout << "gcd2(30, 45) => " << gcd2(30, 45) << std::endl;
  std::cout << "gcd(30, 72, 12) => " << gcd(30, 72, 12) << std::endl;
  std::cout << "lcm2(30, 72) => " << lcm2(30, 72) << std::endl;
  std::cout << "lcm2(30, 42, 72) => " << lcm(30, 42, 72) << std::endl;

  return 0;
}

二分法[編集]

このC++のコードは、二分法(Bisection Method)を使用して関数の根を見つける方法を示しています。以下にコードの各部分の説明を示します。

#include <cmath>
#include <iomanip>
#include <iostream>

/**
 * 2分法による方程式の数値解を求める関数
 * @param low 下限値
 * @param high 上限値
 * @param f 数値解を求める対象となる関数
 * @return 方程式の数値解
 */
double bisection(double low, double high, double (*f)(double)) {
  // 2分法による数値解の計算
  double x = (low + high) / 2; // 中点を計算
  double fx = f(x);            // 中点における関数の値

  // 数値解の精度が十分に高い場合、現在の中点を解として返す
  if (std::abs(fx) < 1.0e-10) {
    return x;
  }

  // 中点の関数の値が0より小さい場合、上限を中点に更新
  // そうでなければ、下限を中点に更新
  if (fx < 0.0) {
    low = x;
  } else {
    high = x;
  }

  // 更新された範囲で再帰的に2分法を適用
  return bisection(low, high, f);
}

/**
 * テスト用の関数
 */
int main() {
  // x - 1 の場合のテスト
  std::cout << std::setprecision(17)
            << bisection(0, 3, [](double x) { return x - 1; }) << std::endl;

  // x^2 - 1 の場合のテスト
  std::cout << std::setprecision(17)
            << bisection(0, 3, [](double x) { return x * x - 1; }) << std::endl;

  return 0;
}
旧課程(-2012年度)高等学校数学B/数値計算とコンピューター#2分法の例を C++ に移植しました。

このC++コードでは、関数ポインタを使用して関数を引数として渡す方法と、lambda式を使用して匿名関数を渡す方法の2つの方法を示しています。

脚註[編集]