c++
コンストラクタ
mallocもnewも必要なメモリ領域をヒープからもらってくるが、mallocには、コンストラクタを呼び出す機能はない。 mallocは仮想関数テーブル迄もを初期化させる。
コピーコンストラクタ:
まず、代入と、初期化との区別ができているかどうか? 初期化とは、変数を定義すると同時に代入する行為のこと。 int a = 4; //初期化 a = 5; //代入 仮引数が作られる時も初期化がされる。 int main(void) { //;;; func(a); //func(a)が呼ばれる } int num = a; という形で初期化が行われ、funcが実行される。 void func(int num) { // } 違いが顕著にでるのは、コンストラクタでの振る舞いである。 class C { public: C(int num) : mNum(num) { //初期化 mFlag = ( (mNum>0) ? 1 : 0 ); //代入 } private: int mNum; bool mFlag; };
コピーコンストラクタは初期化が起こるときに呼び出される。
(自作クラスをnewする時は、コンストラクタとコピーコンストラクタの2つが一緒に呼び出される。
特にクラスをシングルトンにしたいときは必ず、コピーコンストラクタの定義を明示的に書くこと。(何もしない処理を書く)
↑
はまったので注意。
シングルトンの時はインスタンスの生成が特殊なので、constを付けることは基本的にはできない。
コピーコンストラクタをprivate:側で書くと、もし、引数の受け渡しが行われてもコンパイルエラーになる。
何も処理させたくないときに書くと堅牢なコードを書ける。
つまり、仮引数が作られる時と、変数を定義すると同時に代入するとき。
メンバ変数がポインタの場合にはコピーコンストラクタを明示的に書いてあげないといけない。
仮引数がクラスで値渡しのとき注意が必要。(メンバ変数がポインタの場合)
また、
Class C;
C a = b; //初期化しているので、この時コピーコンストラクタが呼ばれる。(メンバ変数がポインタの時に注意。)
コピーコンストラクタの引数が参照である理由
引数が参照でなければ、コピーコンストラクタで値のコピーが行われることになる。
つまり、コピーコンストラクタを呼び出すためにまたコピーコンストラクタを呼び出さなければならなくなる。-->無限ループ
コピーコンストラクタの浅いコピーとディープコピー
浅いコピー Class C { public: C(C& m) { ptr = m.ptr; } } ディープコピー(ポインタもその実体も別に新しく作る) Class C { public: C(C& m) { ptr = new char[mNum]; for(int i=0;i<mNum;i++) { this->ptr[i] = m.ptr[i]; } } private: int* ptr; int mNum; }
フレンド関数
クラス側でfriendを付けて宣言する。
フレンド関数はそのクラスのあらゆるメンバを利用できる。(その関数はクラスのメンバ関数ではないことに注意。
フレンド関数は複数のクラスで定義可能。
2つの別のクラスを比較したいとき、
二項演算子の第一引数は同じクラスのオブジェクトに限定されるが、friend指定すればその制限が取れ、
自由な型を指定できる。
インライン
インラインなんていnらiん。速度を気にする時だけ使えばよいと思う。
静的メンバ関数:
静的メンバ関数の場合も、性的メンバ変数と同じく、インスタンスを生成する前から存在します。 そのため、インスタンスを生成しなくても、呼び出すことが可能。
これが原因で、静的メンバ関数からは、静的でないメンバ変数にアクセスすることができません。
関数の仮引数を配列形式で書いていたら,それはポインタ変数となります.
aは,「3個のintの配列」のポインタで,a = b;としてよい.
a[0]は,3個のintの配列
a[0][0]は,1個のintの値
switch文 caseのあとに変数宣言をしたい場合には、{}をいれるとうまくいく。
二次元配列を扱う場合,二種類の方法があります。 (i) int (*ptr_array)[3]; /* 宣言 */ ptr_array = malloc((sizeof(int) * 3) * 2); /* 確保 */ これは,int array[2][3];と同等のことを,動的に確保した場合です。 最高次以外の配列の数が,コンパイル時に既知である必要があります。 (ii) int i; int **ptr_array; /* 宣言 */ /* 確保 */ ptr_array = malloc(sizeof(int *) * 2); for (i = 0; i < 2; ++i) ptr_array[i] = malloc(sizeof(int) * 3); C++では int data=4; char** ppA; ppA = new char*[data]; for(int i=0;i ppA; } delete ppA; これは,たいちうさんの書かれた方法で, 動的に2次元配列を作成するときに一般的に使われている方法です。 コンパイル時に配列の数が決定している必要はありません。 ptr_array[0]とptr_array[1]で含まれる配列の数を変えることもできます。 関数は,単一の方法で両方を受け取ることはできません。 void func (int array[][3]); または void func (int (*array)[3]); であれば,(i)またはarray[2][3]を渡せます。 一次元目の要素数はどの様にして関数に知らせれば良いか? 1. 要素数を渡すのではなく、配列の最後にそれと分かるデータを入れる 2. 配列の要素数を別の引数で指定する 3. 配列の要素数をマクロ定義する void func (int **array); であれば,(ii)のみを渡せます。 関数内でのみ使うのであれば,使いやすい方を使えばいいですが, 他の関数に渡す場合には,その関数に合わせてメモリを確保する必要があります。
もし2次元配列を以下の関数に渡すと int array[NROWS][NCOLUMNS]; f(array); 関数の宣言は以下のどちらかでないといけない。 f(int a[][NCOLUMNS]) { ... } あるいは f(int (*ap)[NCOLUMNS]) /* apは配列へのポインター */ { ... } char str[6] = "zedse"; char foo[6] = "abcde"; char (*p)[6]; p = (char (*)[6])malloc((sizeof(*p) * 6) * 2); p=&foo; ++p = &str;
参照 初期化が必要 さすものを変えられない 添え字はつけられないし、数字を足して先の要素をさすようにしたりすることもできない。 => 絶対に何かをさしているので、初期化忘れバグが起こらない 指しているものを変えることができないので、間違いが起こらない。 配列アクセスができないので、間違いが起こらない
constについて const で定義された変数は、定数なので、値を変更できませんが、 最初の定義(初期化時)のときにだけは、値を設定することができる const int* const m = &iA; 前半のconstはポインタの指す変数が定数であることを示す。 後半のconstはポインタ自体が定数(変更できない)ことを示す。 クラスでの挙動 自前で定義するクラスのオブジェクトを const にしようとする場合 メンバ関数の定義に const キーワードをつけて、 コンパイラに「この関数はメンバ変数を変更しない」ということを知らせるようにします。 ->メンバ変数を変更しないとはっきりわかっている関数を作ったとき (Get〜()関数など)はconst指定するように心がける。 クラスが適切に const 付のメソッドを提供していないと, そのクラスを利用する側も連鎖的に const を使用できない const std::string& get_name() const { return name_; } //戻り値は初期化するとき以外は変更できないというのが、一つ目のconstの意味。 const string n_name = get_name() //としか書けない。 void func(const int a) { Show(a); //Show(a)がconstメンバになってなきゃいけない。(aの値を変えるかどうか不明なので) } void func(const string* str) { str->????; //????の部分の関数はconstメンバ関数でなければならない。 } constメンバ関数は:メンバ変数がポインタの場合「定数ポインタ」でなければならない。 -->定数ポインタがメンバ変数のアドレスを保持していたら変更できる! void Map::drawCell(int x, int y, int size) const{ //mImageがポインタであれば、drawがconstメンバ関数でなくても問題ない。(constでないメンバ変数にもアクセスできる。) mImage->draw(size*i,0,x*size,y*size,size,size); //mImageがポインタであれば、drawがconstでなくても問題ない。 }
クラステンプレートの設定例 template<class T> class Array2D { private: T* mArray; int mSize0; int mSize1; public: Array2D(); //: mArray(0) ~Array2D(); void setSize(int size0, int size1); //関数呼び出し演算子を再定義。 T& operator()(int index0, int index1); const T& operator()(int index0, int index1) const; //const修飾子のArray2Dインスタンス対策 }; template<class T> Array2D<T>::Array2D() : mArray(0) { } template<class T> Array2D<T>::~Array2D() { delete[] mArray; mArray = 0; } //Array2D<T>の<T>を書くのを忘れずに!! template<class T> void Array2D<T>::setSize(int size0, int size1) { if(mArray) { delete[] mArray; mArray = 0; } mSize0 = size0; mSize1 = size1; mArray = new T[size0 * size1]; } template<class T> T& Array2D<T>::operator()(int index0, int index1) { return mArray[index1 * mSize0 + index0]; } template<class T> const T& Array2D<T>::operator()(int index0, int index1) const { return mArray[index1 * mSize0 + index0]; } 列挙型はクラスのように宣言と定義を分けることができない。
「実体」を持つ場合、クラスの「定義」が必要になる。一方、「ポインタ」を持つ場合は、クラスの「宣言」だけでいい。 また、関数の引数や戻り値に使う場合も宣言だけでよい。 名前なし名前空間 namespace{ int foo; } なら、 namespace a4823678430 { int foo; } using namespace a4823678430; というのと同じ。 効果:そのcpp以外では使わないとわかっているようなクラス、関数を他から(どういう方法であれ)絶対に見えない形で 定義することができる。 privateに宣言してもいいが、名前なし名前空間を設定することで以下のメリットがある。 (1):名前なし名前空間に関数がセットされている場合、 その関数の仕様がしょっちゅう変わるときは(たとえば、引数を加えたいとき) ヘッダを更新する必要があり、ほかのcppでそのヘッダを使用している場合には、そのcppもコンパイルしなおさなければならない。 extern を用いても、他のファイルから無名名前空間にアクセスすることはできない。 いちおう、staticの代わりに名前なし名前空間を使うことが推奨されている。
staticについて
クラス内のstatic
private staticとする場合はシングルトンパターンが思いつくが、
そのほかにメンバ変数にprivate staticを使ったら、生成されたインスタンス経由でしかアクセスできない?
(シングルトンパターンを使う場合、必ず、createとdestroyは一回だけしか呼べないことに注意すること
(いつの間にか呼んでいて間違いに気づかない場合がある)
テンプレートについて テンプレートクラスの関数を使う場合は、その関数の中身が見えていないといけない つまり、cppの中に中身を書くと、ほかのcppから見ることができなくなってしまうので、 ヘッダファイルの中に書かなければならない。 つまり、宣言を~~.hに書いて、内容を~~.cppに書くとlinkエラーになる。 (コンパイルは通ってしまうが、mainにテンプレートで作成された関数を置けなくなる)<−−−はまったので注意。
ファイル関係について テキストデータにおいては実は文字コード&制御コードの割り振りが一通りではないため、 パソコンやその他のコンピュータにデータを移す場合にコードの変換が行われる必要があるのです。 非テキストデータにおいては勝手な変換を行われると困るわけで、無変換に転送される 非テキストデータのことをバイナリデータと呼ぶ
VC++のことについて
プロジェクト作成 プロジェクト内部にディレクトリが構築されているとき プロジェクト->プロパティ->VC++[全般]->追加のインクルードディレクトリでパスを追加。手動で こうすることにより、cppの場所により (内部にディレクトリがないばあいは、vc++が自動的にcppと同じディレクトリを 自動でインクルードパスに含めてくれていたので、includeファイルを開けません、ということにはならない。) ソリューションの中にプロジェクトが複数あるとき ビルドするときはプロジェクト右クリック->スタートアッププロジェクトに設定でビルドしたいプロジェクトが太字になる 原因がよくわからないが、フォルダわけしたいときは、エキスプローラから直にファイルを移動するのではなく、 ソリューションエキスプローラを利用してファイルを移動したほうが、 projファイルの内容がおかしくならないので、そっちのほうがよさそう。 やりかたとしては、ソリューションエキスプローラのアイコン「すべてのファイルを表示」をOnの状態にして、ファイルを作成すればよい。