これからOpenFOAMのC++を学ぶために基本的な内容をメモとして残しておきます。
OpenFOAMはC++プログラミング言語により実装されたCFDに必要な機能がまとめられたオブジェクト指向の考え方で設計されているツールです。
C++の基本的な内容を理解する。
※クラスの定義は出てきません(次回の内容とします)。
C++の全てを学ぼうとすると膨大過ぎてとても扱いきれないので、必要最低限知っておくとよい内容を簡単にまとめていきます。
出力ストリーム
ファイル名を「main000.cpp」として以下のように書きます。
mai000.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int main() { int a = 5; // integer double pi = 3.1415926; //double std::cout << "some basic srithmetic..." << std::endl; std::cout << a << " + " << pi << " = " << a+pi << std::endl; std::cout << a << " * " << pi << " = " << a*pi << std::endl; return 0; } |
これをg++でコンパイルすると「a.out」という実行ファイルが作られるので、コマンドで
1 |
./a.out |
と打って計算実行します。
【結果】
1 2 3 |
some basic srithmetic... 5 + 3.14159 = 8.14159 5 * 3.14159 = 15.708 |
「main000.cpp」の1行目に出てくる、#includeはiostreamというヘッダファイルを読み込むための宣言です。
1 |
#include <iostream> |
複数人でプログラムの開発をする場合に、変数名がかぶらないように名前空間で管理することができます。
1 |
std::cout |
とすることで名前空間stdのcout(コンソール画面に出力するための出力ストリーム)を呼び出すことができます。
出力ストリーム(標準名前空間の利用)
1 |
std::cout |
と毎回書くのがわずらわしい場合には、「標準名前空間を利用する」という宣言「using namespace std;」と書くことで省略した書き方が可能です。
mai001.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> using namespace std; int main() { int a = 5; // integer double pi = 3.1415926; //double cout << "some basic srithmetic..." << endl; cout << a << " + " << pi << " = " << a+pi << endl; cout << a << " * " << pi << " = " << a*pi << endl; return 0; } |
using namespace std;の部分ですが、using namespaceは、指定された名前の名前空間を使うことを意味しています。
別の名前空間
複数人でプログラムを開発している場合に、意図せずに同じ変数名を使ってしまう場合があります。この場合に、先ほど出てきたように別の名前空間内で変数を設定しておくことで、変数名が干渉せずに使用することができます。
名前空間は、
1 2 3 |
namespace 名前{ // コード } |
のように書きます。
main002.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <iostream> namespace Foam1{ int a = 5; double b = 10.5; } namespace Foam2{ int a = 6; double b = 16.5; } int main() { int a = 5; // integer double b = 3.1415926; //double std::cout << "namespace ..." << std::endl; std::cout << a << " + " << b << " = " << a+b << std::endl; std::cout << Foam1::a << " + " << Foam1::b << " = " << Foam1::a +Foam1::b << std::endl; std::cout << Foam2::a << " + " << Foam2::b << " = " << Foam2::a +Foam2::b << std::endl; return 0; } |
名前空間内の変数を呼び出す際には「 Foam1::a」のように書きます。
【結果】
1 2 3 4 |
namespace ... 5 + 3.14159 = 8.14159 5 + 10.5 = 15.5 6 + 16.5 = 22.5 |
今回、別の名前空間「Foam1」「Foam2」内に同じ変数名「a」「b」を定義しましたが、出力結果を見ると別の名前空間内での変数は干渉することなく出力できています。
名前空間内は変数以外にも、関数やクラスも定義できるのですが、追々練習でやっていきます。
スコープ
変数には賞味期限のようなものがあって、有効になる範囲というのがあります。
main002.cppに以下を追加しました。
1 2 3 4 5 6 7 |
// aの範囲は{}内のみ { int a = 100; std::cout << "scope..." << std::endl; std::cout << "a = " << a << std::endl; } std::cout << "a = " << a << std::endl; |
main003.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <iostream> namespace Foam1{ int a = 5; double b = 10.5; } namespace Foam2{ int a = 6; double b = 16.5; } int main() { int a = 5; // integer double b = 3.1415926; //double std::cout << "namespace ..." << std::endl; std::cout << a << " + " << b << " = " << a+b << std::endl; std::cout << Foam1::a << " + " << Foam1::b << " = " << Foam1::a +Foam1::b << std::endl; std::cout << Foam2::a << " + " << Foam2::b << " = " << Foam2::a +Foam2::b << std::endl; // aの範囲は{}内のみ { int a = 100; std::cout << "scope..." << std::endl; std::cout << "a = " << a << std::endl; } std::cout << "a = " << a << std::endl; return 0; } |
【結果】
1 2 3 4 5 6 7 |
namespace ... 5 + 3.14159 = 8.14159 5 + 10.5 = 15.5 6 + 16.5 = 22.5 scope... a = 100 a = 5 |
{}内の「int a = 100;」は{}でしか有効であり、{}の外に出るとmain()関数の一番初めの「int a = 5;」が有効になっているのが確認できます。
関数
まとまった処理に関しては関数を作っておくと便利です。
値渡し
main004.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> void counter_func(int counter) { ++counter; std::cout << "値渡しになるので注意" << std::endl; std::cout << "counter " << counter << std::endl; std::cout << "counter id " << &counter << std::endl; } int main() { int a_cout = 0; counter_func(a_cout); //関数の呼び出し std::cout << "a_count " << a_cout << std::endl; std::cout << "a_count id " << &a_cout << std::endl; std::cout << "===========" << std::endl; return 0; } |
【結果】
1 2 3 4 5 6 |
値渡しになるので注意 counter 1 counter id 0x7ffd8e4f197c a_count 0 a_count id 0x7ffd8e4f1994 =========== |
「counter_func(a_cout);」で変数「a_cout」を関数に引き渡して関数を呼び出しています。
ここで注意する点は、
1 2 3 4 5 6 7 |
void counter_func(int counter) { ++counter; std::cout << "値渡しになるので注意" << std::endl; std::cout << "counter " << counter << std::endl; std::cout << "counter id " << &counter << std::endl; } |
counter_func()関数内の「counter」と、main()関数内の「a_count 」は別のメモリ領域に値を格納しているため、counter_func()関数内で「++counter」により値が1増えるのですが、たちまちcounter_func()関数の外に出るとmain()関数内の「a_count 」は相変わらず1だということです。
これを値渡しと言います。
参照渡し
引数に&をつけることで同じメモリ領域を参照して引数として渡すことができます。
1 2 3 4 |
void counter_func1(int& counter) { // コード } |
main005.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <thread> void counter_func(int counter) { ++counter; std::cout << "値渡しになるので注意" << std::endl; std::cout << "counter " << counter << std::endl; std::cout << "counter id " << &counter << std::endl; } void counter_func1(int& counter) { ++counter; std::cout << "参照渡し" << std::endl; std::cout << "counter " << counter << std::endl; std::cout << "counter id " << &counter << std::endl; } int main() { int a_cout = 0; counter_func(a_cout); std::cout << "a_cout " << a_cout << std::endl; std::cout << "a_cout id " << &a_cout << std::endl; std::cout << "===========" << std::endl; counter_func1(a_cout); std::cout << "a_cout " << a_cout << std::endl; std::cout << "a_cout id " << &a_cout << std::endl; std::cout << "===========" << std::endl; return 0; } |
【結果】
1 2 3 4 5 6 7 8 9 10 11 12 |
値渡しになるので注意 counter 1 counter id 0x7ffe55b17e4c a_cout 0 a_cout id 0x7ffe55b17e64 =========== 参照渡し counter 1 counter id 0x7ffe55b17e64 a_cout 1 a_cout id 0x7ffe55b17e64 =========== |
参照渡しの関数では「counter 」と「a_cout」が同じidになっているのが確認できます。
なので、「a_cout」がcounter_func1()関数に渡されたときには「++counter」によって変わった値 は「a_cout」と同じidに格納されるので、counter_func1()関数の外に出てると「a_cout」の値は1になっています。
関数処理を行いたい場合に、無駄駄にメモリを消費しないので良いのですが、思いもよらないところで値が変わっている可能性があるので注意が必要ですね。
関数に配列を渡す
配列を関数に渡す場合は配列そのものを関数に渡してしまうと、無駄にメモリ領域を食うことになるので配列の先頭のアドレスをポインタとして渡すことでメモリ消費を抑えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> //配列を渡したい場合 int biggest_func(int *a, int n_element) //配列の先頭のアドレスを受け取る // int& biggest(int array[], int n_element) { int biggest = 0; std::cout << "array first id value " << *a << std::endl; std::cout << "n_element " << n_element << std::endl; for (int i = 0; i < n_element; i++) { std::cout << "a[" << i << "] = " << a[i] << std::endl; if (a[biggest]<a[i]) { biggest = i; } } std::cout << "biggest of array is " << biggest << std::endl; std::cout << "array[" << biggest << "] = " << a[biggest] << std::endl; return a[biggest]; } int main() { int array[5] = { 14, 2, 5000, 4, 3 };//配列 int n = sizeof(array) / sizeof(int); // 要素の数 int max = biggest_func(array, n); std::cout << "array = " << max << std::endl; return 0; } |
【結果】
1 2 3 4 5 6 7 8 9 10 |
array first id value 14 n_element 5 a[0] = 14 a[1] = 2 a[2] = 5000 a[3] = 4 a[4] = 3 biggest of array is 2 array[2] = 5000 array = 5000 |
ここでは、main()関数内ではじめに配列を定義しています。
1 |
int array[5] = { 14, 2, 5000, 4, 3 };//配列 |
要素の数は、「 sizeof(array) => 20」で配列全体の大きさを整数型の要素の大きさ「sizeof(int) => 4」で割ることで取得できます。
1 |
int n = sizeof(array) / sizeof(int); // 要素の数5 |
配列を関数に渡すときは、配列の先頭のアドレスをポインタとして渡しています。
1 2 3 4 |
int biggest_func(int *a, int n_element) //配列の先頭のアドレスを受け取る { //コード } |
途中で、
1 |
std::cout << "a[" << i << "] = " << a[i] << std::endl; |
のようにして配列の値を出力指定していますが、これは配列の初めの要素のポインタaに対してa[i]の指す値を取得しています。
1 2 3 4 5 |
a[0] = 14 a[1] = 2 //a[1]で1つポイントをずらしたポイントの値の取得 a[2] = 5000 //a[2]で2つポイントをずらしたポイントの値の取得 a[3] = 4 //a[3]で3つポイントをずらしたポイントの値の取得 a[4] = 3 //a[4]で4つポイントをずらしたポイントの値の取得 |
参考記事
OpenFOAMの簡単なコードを書いてカスタマイズ練習ができる内容を挙げておきます。
C++の基礎を学びたいときにとても参考になる動画を挙げておきます。
OpenFOAMに特化した内容を学びたい場合は以下の記事が参考になります。
参考書
持っている参考書をC++の書籍を載せておきます。
↑こちらは初めにC++を学ぶにはちょうど良い内容かと思います。
実はこちらに書籍の内容がまとめられています。
↑基礎が身に付いたら体系的に学ぶために持っていても良いと思う書籍です。
「現行の規格に準拠したC++プログラムの書き方を徹底的に解説していく」と謳っているように内容も中級者以上を対象にした本格的なC++の書籍です。
実はこちらに本の内容がそのままアップされています。