c++ 久しぶりなので復習
デストラクタ
コンストラクタで動的にメモリを確保したときなどは、手動でメモリを開放しなければならない。
そうしたときに、デストラクタを定義する必要がある。
コピーコンストラクタ
オブジェクトが他のオブジェクトで初期化される場合に呼び出されるコンストラクタ。
初期化の場合はコピーコンストラクタが呼び出され、代入の場合には代入演算子による処理が行われる。
関数に実引数を渡す場合も、実引数によって仮引数が初期化されるので、コピーコンストラクタが必要。
初期化したい場合は、コピーコンストラクタを定義して、新しくメモリを確保する。
コピー元とコピー先のアドレスがかぶってしまうのを防ぐ。
定義の仕方は
クラス名::クラス名(const 参照型 引数) Car::Car(const Car& c)
なぜ、参照型にする必要があるかというと、仮に参照型でなく普通の型名にしてしまうと、値渡しが行われるが、
いま、コピーコンストラクタで初期化についての定義を読み込もうとしているので、トートロジーになってしまうから。
初期化と代入は違うものであることをしっかり認識すること。
ポインタ
#include <iostream> using namespace std; void swap(int* pX, int* pY); int main() { int a = 5; //int* はポインタだよって宣言してるだけ。 //ポインタを宣言した時点では、構造体の実体がメモリ上に存在していないことに注意。 int* pAnd; pAnd = &a; //アドレスを入れるための箱。 cout << pAnd << "\n"; //この"*"は間接参照演算子。アドレスに対応する変数の値を表示する。 cout << *pAnd << "\n"; //この"*"も間接参照演算子。 値を書き換えるとaに反映される。→値渡し(swap関数など) *pAnd = 50; int num1 = 5; int num2 = 50; swap(&num1, &num2); cout << num1 << "and" << num2 << "\n"; cout << a << "\n"; return 0; } //ここでの"*"はポインタだよって宣言してるだけ。つまり、アドレスを入れるための箱ですよって宣言しただけ。 void swap(int* pX, int* pY) { int tmp; //つぎの"*"は間接参照演算子。pXがアドレスだから、*pXはアドレスの場所の値である。 tmp = *pX; //アドレスpXに*(間接参照演算子)がついて*pXは値を意味する。 //*pYという値を突っ込んだら、*pXは値を変えるが、*pXはnum1とリンクしているから、 //num1の値も書き換えられる。(*pXとnum1とはまったく同じもの。) //注意:アドレスpXそのものは書き換わらない。 *pX = *pY; *pY = tmp; }
前 後 n1 n2 n1 n2 5 50 50 50 | | | | | | -> | | | | | | pX pY pX pY 100 110 100 110
配列
参照
int main() { int num1 = 5; int num2 = 50; cout << "変数num1の値は" << num1 << "\n"; cout << "変数num2の値は" << num2 << "\n"; swap(num1, num2); cout << num1 << " and " << num2 << "\n"; return 0; } //参照 変数などで初期化した識別子のこと。 //xとかy自体はアドレスではなく、ただの数。 //参照とローカル変数は何が違うかというと、参照の宣言をしたものは、リンクすることができる。 //この場合、xを参照とすることにより,x(アドレスではなく、数)とnum1がリンクしている。 //yも同様。 void swap(int& x, int& y) { int tmp; tmp = x; //xの値が書き換わると、同時にリンクされているnum1の値も書き換わる。 x = y; y = tmp; } /* //参照ではなく、ローカル変数にすると、うまく動作しなくなる。 void swap(int x, int y) { int tmp; tmp = x; //xの値が書き換わっても、num1とはリンクしていないので、num1の値は書き換わらない。 x = y; y = tmp; //すべての処理が終了すると、xとyはローカル変数なので、メモリが開放される。 //main内のnum1とnum2はまったく変化せずにmain関数へと戻る。 } //ポインタと参照とは概念的にはまったく違うことに注意すること。 */
クラス関係
#include <iostream> using namespace std; //コンストラクタ:オブジェクトの初期化 //クラスからオブジェクト(インスタンス)が生成されるときに、自動的に最初に呼び出される。 //オーバーロード可能。 class Car { private: //クラス全体で扱うデータを格納しておくデータメンバが静的データメンバ static int sum; //静的データメンバ::クラス全体に関連付けられている。 int num; double gas; //メンバはオブジェクトに関連付けられている。 public: static void showSum(); Car(); //コンストラクタ Car(int n, double g); //引数を二つ持つコンストラクタ。 オーバーロードしていることに注意。 //デフォルト引数も使える。 //Car(int n = 0; double g = 0); void show(); }; //コンストラクタを省略した場合には、コンパイラが用意したからのデフォルトコンストラクタが呼び出される。 //ただし、コンストラクタを一つでも自分で定義した場合には、デフォルトコンストラクタが用意されない。 int main() { Car car1; car1.show(); Car::showSum(); //静的メンバ関数はクラス名をつけて(オブジェクト名ではなく)呼び出す。 Car car2(12334, 20.5); car2.show(); Car car[4]; Car::showSum(); return 0; } int Car::sum = 0; //private::内にあるのにアクセスできてしまうのはなぜ?? Car::Car() { num = 0; gas = 0.0; sum++; cout << "車作成\n"; } Car::Car(int n, double g) { num = n; gas = g; sum++; cout << "車作成\n"; } void Car::showSum() { cout << "車は全部で" << sum << "台あります。\n"; //注意::通常のデータメンバ(この場合だとnumやgas)にはアクセスできない。 } void Car::show() { cout << num <<"::::\n"; cout << gas << "::::\n"; }
継承とか。
#include <iostream> using namespace std; class Car { protected: int num; double gas; public: Car(); Car(int n, double g); void setCar(int n, double g); void show(); }; class RacingCar : public Car { //派生クラスでも通常どおり、基本クラスのアクセス指定子に従って動作する。 private: int course; public: //何も指定が無ければ、派生クラスのコンストラクタ内の先頭で //基本クラスの引数の無いコンストラクタを呼び出す。 //基本クラスのコンストラクタ自体は派生クラスに自動的には継承されないが //基本クラスの引数なしのコンストラクタが自動的に呼び出される。 RacingCar(); RacingCar(int n, double g, int c); //派生クラスで基本クラスとまったく同じ関数名、引数の数、型を持つメンバ関数を定義することができる。 //この場合、派生クラスで定義したメンバ関数が基本クラスのメンバに変わって機能する。このことをオーバーライドという。 void show(); void setCourse(int c); }; //Car の記述 Car::Car() { num = 0; gas = 0.0; cout << "create a car\n"; } Car::Car(int n, double g) { num = n; gas = g; cout << "number:" << num << "gas:" << gas << "の車を作成\n"; } void Car::setCar(int n, double g) { num = n; gas = g; cout << "The numbers are setted::" << num << ":" << gas << "\n"; } void Car::show() { cout << "number:" << num << "\n"; cout << "gas:" << gas << "\n"; } //RacingCarの記述 RacingCar::RacingCar() { course = 0; cout << "Racing Car" << "\n"; } //基本クラスの引数2このコンストラクタが呼び出されるようにする。 //RacingCarはCarを継承していることはすでに記述してあるので、":"のあとにはCarと書きさえすればよい。 RacingCar::RacingCar(int n, double g, int c) : Car(n, g) { course = c; cout << "コース番号" << course << "のレーシングカーを作成しました。\n"; } void RacingCar::setCourse(int c) { course = c; //基本クラスのprivateメンバは派生クラス内からもアクセスすることはできない。 //アクセスしたければ、protectを使用する。 //protectを利用すると、外部からアクセスすることはできないが、派生クラスの内部からだけはアクセスすることができる。 //gas = 100; cout << "コース番号を" << course << "にしました。\n"; } void RacingCar::show() { cout << "number::" << num << "\n"; //アクセス指定子がprotectedなので、numにアクセスすることができる。 cout << "gas::" << gas << "\n"; cout << "course::" << course << "\n"; } int main() { Car car1; RacingCar rccar1; //()はいれない。 RacingCar rccar2(1234, 20.5, 5); Car* pCars[2]; //基本クラスのポインタを使うと、基本クラス自身だけでなく、派生クラスのオブジェクトをさすことができる。 pCars[0] = &car1; //アドレス pCars[0]->setCar(1234, 20.5); //基本クラス pCars[1] = &rccar1; pCars[1]->setCar(4567, 30.5); //派生クラス pCars[0]->show(); //基本クラスのポインタが派生クラスのオブジェクトを指していても、 //基本クラスのshow()関数が呼び出されてしまっている。 pCars[1]->show(); return 0; }
#include <iostream> using namespace std; class Vehicle { protected: int speed; //派生クラス内でアクセスしたいので、protected. public: Vehicle(); void setSpeed(int s); virtual void show() = 0; //純粋仮想関数。 //クラスの宣言内にこのような純粋仮想関数を一つでも持つクラスはオブジェクトを作成することができない。 //このようなクラスを抽象クラスという。 }; //抽象クラスを拡張した派生クラスはどれも、 //抽象クラスの純粋仮想関数(この場合はshow()メンバ関数)と同じ名前のメソッドを持つ必要がある。 //めりっと::一貫性のあるコードを記述することができる。 class Car : public Vehicle { private: int num; double gas; public: //コンストラクタを一切明示しなければ、引数の無いデフォルトコンストラクタが呼び出されるが、 //ここでは明示されているので、デフォルトコンストラクタは存在しない。 Car(int n, double g); void show(); }; class Plane : public Vehicle { private: int flight; public: Plane(int f); void show(); }; //Vehicleクラスのメンバの定義 Vehicle::Vehicle() { cout << "抽象クラスのコンストラクタ呼び出し\n"; } void Vehicle::setSpeed(int s) { speed = s; cout << "速度を" << speed << "にしました\n"; } //Vehicleクラスではshow関数の内容は記述する必要は無い。 //Carクラスメンバ関数の定義 Car::Car(int n, double g) { num = n; gas = g; cout << "number:" << num << "gas:" << gas << "\n"; } void Car::show() { cout << "number:" << num << "です\n"; cout << "gas:" << gas << "です\n"; cout << "vehicle:" << speed << "です。\n"; } //Planeクラスメンバ関数の定義 Plane::Plane(int f) { flight = f; cout << "number::" << flight << "の飛行機を作成\n"; } void Plane::show() { cout << "flight number::" << flight << "\n"; cout << "vehicle::" << speed << "\n"; } int main() { //Vehicle vc とはできない。 Vehicle* pVc[2]; //この時点で抽象クラスのコンストラクタが作成される。 Car car1(1234, 20.5); pVc[0] = &car1; pVc[0]->setSpeed(60); Plane pln1(232); pVc[1] = &pln1; pVc[1]->setSpeed(500); pVc[0]->show(); pVc[1]->show(); return 0; }