クラスについて。
アップキャスト
キャストする時、派生クラスのオブジェクトは基底クラスへの参照に渡すことができる。 継承木の上の方にあるクラスへのキャストなので、アップキャストという。
protectedアクセス指定子
protected:指定すると、それ以降のメンバは、外部には公開されないが、 派生クラスには公開される。
仮想関数
クラス定義内でvirtualを宣言の前につけると、 基底クラスの関数や、参照、ポインタを通しても、その本来の方によって、適切に呼び分けられる。 デコンストラクタの時はvirtualをつけておくべき。 2011.3.29追記 仮想関数の宣言virtualはprivate内では使えない。 2011.3.30追記 基底クラスでvirtualをつけると、派生クラスでオーバーライドしたときに、その関数の能力が引き継がれるというわけではない。 なので、void Disp() { CBook::Disp(); cout << "著者:" << m_pszAuther << endl; cout << endl; }
のように付け加えないといけない。 仮想関数が生きてくるのは、bool Stream::Set() { SetBase() return m_n; }
という風に、基底クラスでこのように定義がされ、 オーバーライドされなかったときに、ChildStream::Set()を呼び出すと、 Set関数の中のSetBase関数はChildStream::SetBase()として呼び出される。 もちろん、Stream::SetBase()は仮想関数でなければいけないが。
純粋仮想関数
仮想関数の宣言に0を代入する形に書くと、実装しなくなる。
純粋仮想関数を持つクラスは抽象クラスといい、実体を作れない。
たとえ、一つでも純粋仮想関数がオーバーライドされていなければ、派生クラスも抽象クラスになる。
polymorphism
アップキャスト、仮想関数を利用することで、ある変数の本当の型ごとに変更される(基底クラスに左右されない)性質のこと。 一つのモノに、場面に応じた複数の機能がある性格のこと。 例を言えば、「凧が上がる」と「成績が上がる」は同じ「上がる」と表現するけど、 ニュアンス的には「上向きに動く」で同じだが、内容的には意味が違う、と言うような話。 C++ にはポリモーフィズムな性格があるというほうがどちらかというと正しいらしい。
オーバーライド
同じ形の関数を派生クラスで再定義することをオーバーライドという。 virtual指定しなければ、基底クラスの名前空間の関数が呼ばれてしまうので、 基本的にオーバーライドする時はvirtualをつける。
クラス、継承の例。こうみると、C++はセキュアなメタ言語なのもうなずける。
#includeusing namespace std; // 書籍クラス(親クラス) class CBook { //コンストラクタ public: CBook( const char* pszTitle, const int nPrice, int nArrivedValue = 30, int nSelledValue = 0); //仮想関数:普通にオーバーライドしても、 //アップキャストしてしまうと、 //アップキャスト先のクラスのメンバ関数が呼ばれる。 //仮想関数にすると、アップキャストしても、 //本来のクラスのメンバ関数が呼ばれるようになる。 virtual ~CBook(); // 以下は公開しないメンバ protected: const char* m_pszTitle; // タイトル const int m_nPrice; // 値段(円) const int m_nArrivedValue; // 入荷した数 int m_nSelledValue; // 売れた数 int GetSells() const; //売上額 int GetRestValue() const; //残数 // 以下は公開するメンバ public: void Selled(); //一冊売れた //virtual キーワードをつけて、仮想関数にする。 //このような性質をポリモーフィズムと言い、 //そのためにはメンバ関数に virtual を付ける。 //このような動作を動的結合(レイト・バインディング)または、実行時結合という。 visual void Disp(); //状態の表示 }; //コンストラクタ //--------------------------------------------------- //コンストラクタの中ではメンバ変数の初期化を行いますが、 //const を宣言したメンバ変数への代入は許されません。 //その場合、赤字の以下のコードにあるように記述します。 //この動作はオブジェクトの種類を決定するだけの //メンバ変数(この例だと本のタイトルや著者)に使用します。 // //constが付いているので、代入ではなく //初期化をしなければならない。 // //コンストラクタ名(仮引数リスト) : //基底クラス名(実引数リスト) // CBook::CBook( const char* pszTitle, const int nPrice, int nArrivedValue, int nSelledValue) :m_pszTitle( pszTitle ), m_nPrice( nPrice ), m_nArrivedValue( nArrivedValue ) { //constの付いていない変数はかっこ内で処理 m_nSelledValue = nSelledValue; } //デコンストラクタ CBook::~CBook() { } // ----------------------------------- // Summary: // 売り上げの算出 // Returns: // 売り上げ額(円) int CBook::GetSells() const { return m_nSelledValue * m_nPrice; } // ----------------------------------- // Summary: // 残数の算出 // Returns: // 残数 int CBook::GetRestValue() const { return m_nArrivedValue - m_nSelledValue; } // ----------------------------------- // Summary: // 一冊売れた void CBook::Selled() { m_nSelledValue++; } // ----------------------------------- // Summary: // 状態の表示 //virtual キーワードをつけて、仮想関数にする。 void CBook::Disp() { cout << m_pszTitle << "のデータ" << endl; cout << "--------------------------" << endl; cout << "値段:" << m_nPrice << endl; cout << "入荷した冊数:" << m_nArrivedValue << endl; cout << "販売した冊数" << m_nSelledValue << endl; cout << "売り上げ:" << GetSells() << endl; cout << "残冊数:" << GetRestValue() << endl; } // // 一般書籍クラス(子クラス) // //記載するのは、増えたメンバ変数・メンバ関数、 //変更したいメンバ変数、コンストラクタ・デストラクタ class CGeneralBook : public CBook { protected: //外部には公開されないが、派生クラスには公開される。 const char* m_pszAuther; // 筆者(インターフェースの拡張が可能) public: CGeneralBook( const char* pszTitle, const char* pszAuther, const int nPrice, int nArrivedValue, int nSelledValue ); virtual ~CGeneralBook(); void Disp(); //オーバーライド }; // ----------------------------------- // Summary: // コンストラクタ // Arguments: // pszTitle: タイトル // pszAuther: 著者 // nPrice: 値段 // nArrivedValue: 入荷した数 // nSelledValue: 売れた数 CGeneralBook::CGeneralBook( const char* pszTitle, const char* pszAuther, const int nPrice, int nArrivedValue, int nSelledValue ) :CBook( pszTitle, nPrice, nArrivedValue, nSelledValue ), m_pszAuther( pszAuther ) { ; } // ----------------------------------- // Summary: // デストラクタ CGeneralBook::~CGeneralBook(){} // ----------------------------------- // Summary: // 状態の表示 void CGeneralBook::Disp() { CBook::Disp(); cout << "著者:" << m_pszAuther << endl; } // //本体 // // int main() { // オブジェクトの生成 CGeneralBook* pHarryPotter = new CGeneralBook( "ハリーポッター賢者の石", "J.K.ローリング",1900, 52, 0 ); CGeneralBook* pStarWars = new CGeneralBook( "スターウォーズ暗黒の旅路 上巻", "エレイン・カニンガム", 780, 12, 0 ); CMagazine* pGendai = new CMagazine( "月間現代", 780, 8, 0, CMagazine::Monthly, 10 ); CMagazine* pWeeklyASCII = new CMagazine( "週刊アスキー", 350, 20, 0, CMagazine::Weekly, 28 ); // オブジェクトへのポインタ列 CBook* BooksArray[4] = { pHarryPotter, pStarWars, pGendai, pWeeklyASCII }; //要素数を求める。 static const int SIZE = sizeof BooksArray / sizeof *BooksArray; // ハリーポッター1冊売れた pHarryPotter->Selled(); // スターウォーズ2冊売れた pStarWars->Selled(); pStarWars->Selled(); // 週刊現代1冊売れた pGendai->Selled(); // 週刊ASCII2冊売れた pWeeklyASCII->Selled(); pWeeklyASCII->Selled(); // 一括表示 for( int i = 0; i < 4; i++ ){ //ここでのよびだしで、Dispを仮想関数にしないと(virtual をつけないと) //親クラスのCBook::Disp() を割り当ててしまう。 BooksArray[i]->Disp(); } // オブジェクトの破棄 //デストラクタを仮想関数にしていない場合、 //CBook::~CBook() が呼ばれてしまい、 //CGeneralBook::~CGeneralBook() や CMagazine::~CMagazine は呼び出されない for(int i = 0; i < 4; i++) { delete BooksArray[i]; BooksArray[i] = NULL; } return -1; }