6章: クラスの継承
元になるクラス(基底クラス)から新しいクラス(派生クラス)を作ることを継承という。継承するときには基本的にはメンバ関数や変数も継承される。複数のクラスから新しいクラスを作ることもできるが、それを多重継承という。派生クラスで関数を再定義したら上書きされる。(スコープ解決演算子を使えばアクセスはできる)
class Food { public: void SetPrice(int myprice); int GetPrice(); private: int price; }; class Fruit : public Food { public: void SetFarmerName(const std::string farmer); std::string GetFarmerName(); void PrintPrice() { std::cout << price; // コンパイルエラー } private: std::string farmer; }; int main() { Fruit banana; banana.SetFarmerName("ossan"); std::cout << banana.GetFarmerName() << std::endl; }
- 基底クラスのメンバへのアクセス
public: 派生クラスからでもアクセスできる。他のクラスや関数からもアクセスできる。
private: 派生クラスからはアクセスできない。他のクラスや関数からはアクセスできない。
protected: 派生クラスからはアクセスできる。他のクラスや関数からはアクセスできない。(publicやprivateの間くらい?)
- 多重継承
class Fax : public Phone, public Printer {...};
同名のメンバが基底クラスにあるときはスコープ解決演算子を使う。myFax.Phone::Beep()
- オブジェクトの代入
「Parent <= Child」は可。派生クラス固有のメンバはコピーされない。
- オブジェクトのポインタの代入
「pParent <= pChild」は可。pFood->MemberFunc()とすればメンバ関数にもアクセスできる。
- 仮想関数
派生クラスのオブジェクトの"ポインタ"を基底クラスのオブジェクト"ポインタ"に代入したあとに、メンバ関数を呼ぶことを考える。このときメンバ関数をオーバーライドしていても基底クラスの関数が呼ばれる。この動作が困るときは仮想関数を使う。注意点はポインタでなくオブジェクトそのものだとvirtualを定義していても基底クラスの関数が呼ばれる点。(この機能はキモいのでは)
class A { public: virtual void Bark() { std::cout << 'A'; } // 普通の仮想関数 vitual void Bark() = 0; // 純粋仮想関数 }; class B { // オーバーライド }; int main() { B b; A* pa = &b; pa->Bark(); // "B" }
継承する可能性があるクラスは派生クラスのデストラクタを読んでもらわないといけないのでデストラクタはvirtualにすべき。
- private継承: 継承したメンバは基底クラスでpublicでも派生クラスではprivateになる
7章: クラスについてもう少し
- static
staticな変数は複数のオブジェクトで共通のメモリ領域を持ち、共有される。staticな関数はstaticな変数にのみアクセスする関数。どちらもオブジェクトを生成しなくても::
で呼び出せる。
class Food { public: static double tax; // ここで初期化しない。 }; double Food::tax = 1.05 // 初期化。コンストラクタで初期化しない。 int main() { Food myFood; myFood.tax = 1.03; std::cout << Food::tax; // 1.03 }
class Food { public: static void SetTax() { tax = n; } protected: static double tax; }; Food::SetTax(1.08); myFood.SetTax(1.10);
- オブジェクトをメンバに そもそもIntとかもオブジェクトだし、自作オブジェクトもメンバにできる。メンバオブジェクトの初期化の文法は要チェック。
class Date {...}; class Food { private: Date LimitDate; int price; }; Food::Food() :LimitDate(2010, 4, 1), price(100) // 初期化リストという {...};
配列にも入れられる。
Date myDates[] = {Date(1,1,1), Date(0,0,0)}; class TakeOne { public: TakeOne(int){...}; } TakeOne arr[] = {1, 2, 3}; // コンストラクタの引数がひとつだけなら引数を並べるだけでいい
8章: 上級
このへんはC++11で変わってそうなので注意。
- テンプレート
#defineマクロはより便利なのがtemplate関数である。
template<class T> T max(T a, T b){...} int main() { std::cout << max(1, 2); double a = 1, b = 2.1; std::cout << max(a, b); }
templateを使わなかった場合関数を複数書いてオーバーロードしないといけない。
クラスも作れる。
template<class T> // なんとなくSTLを例にしている。 class vector{ ... }; vector<int> vec; // オブジェクトの生成時に型を指定する
- STL(標準テンプレートライブラリ)
vector、list、queue、stackクラスがあるらしい。
vector<int> v1; v1.push_back(10); v1.push_back(11); // !C++11ではdecltypeが使えたはず for (vector<int>::iterator i = v1.begin(); i != v1.end(); i++){ std::cout << *i; }
// hg1 + hg2, hg1 = hg2を定義したい。 class Hoge { public: Hoge operator+(const Hoge &hg2); Hoge & operator=(const Hoge &hg2); // なぜ&が必要かは分からない。 } Hoge Hoge::operator+(const Hoge &hg2) { // 何か計算 return Hoge(...) // コンストラクタを呼ぶ } Hoge Hoge::operator=(const Hoge &hg2) { if (this == &hg2) return *this; else ... return *this; }
- 型キャスト
Cでは(int*) とか書く。 c++では
const_cast<T>(v)
: constを取り除いてキャストdynamic_cast<T>(v)
: 基底クラスのポインタ、参照を派生クラスへ変換するstatic_cast<T>(v)
: Cと近いreinterpret_cast<T>(v)
: 関数のポインタをゴニョゴニョするのに使うらしい
9章: 付録
ヘッダファイル .hファイルにクラス定義、.cppファイルにメンバ関数
#ifdef
、#define
、#endif
マクロを使おう。例外
// 例外を通知 throw -1; // 例外を処理する try { .... } catch (int &err) { // int型のエラーを受け取る``(...)``とすれば全ての型の例外を受け取れる。 }
参考、感想:
絵を見ながら適当に読めるので簡単でよかった。簡単すぎて具体例のほとんどがgetter、setterになっていた。自分で使うときはデータベースか普通に行列、dataframeで済ませるレベルではある。オブジェクト指向は今のところドットを間に置いてポチポチとつなぐやつ(カプセル化)ぐらいの認識にとどまっている。そのうちクラスの継承も使う時が来ると良いが。終盤にかけて、細かいところやプログラムの記述が大雑把だったので、正確な本も読みたい。