「仮想関数」の版間の差分

提供: C++入門
移動: 案内検索
行12: 行12:
 
[[仮想関数]] を用いない場合、ポインタの型のメンバが呼び出されます。
 
[[仮想関数]] を用いない場合、ポインタの型のメンバが呼び出されます。
 
[[仮想関数]] を使用したときは、派生クラスのポインタを基底クラスのポインタで受け取っても、派生クラスのメンバ関数を呼び出します。
 
[[仮想関数]] を使用したときは、派生クラスのポインタを基底クラスのポインタで受け取っても、派生クラスのメンバ関数を呼び出します。
 +
 +
 +
[[クラス]]を継承する場合には、デストラクタを[[仮想関数]]として定義する必要があります。[[継承と仮想デストラクタ]]をご参照ください。
  
 
== 継承の例 ==
 
== 継承の例 ==
行242: 行245:
 
* [[純粋仮想関数]]
 
* [[純粋仮想関数]]
 
* [[クラス]]
 
* [[クラス]]
 +
* [[継承と仮想デストラクタ]]

2013年3月24日 (日) 12:58時点における版


読み方

仮想関数
かそうかんすう

概要

仮想関数 を用いない場合、ポインタの型のメンバが呼び出されます。 仮想関数 を使用したときは、派生クラスのポインタを基底クラスのポインタで受け取っても、派生クラスのメンバ関数を呼び出します。


クラスを継承する場合には、デストラクタを仮想関数として定義する必要があります。継承と仮想デストラクタをご参照ください。

継承の例

ソースコード extends_0.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                void x() {
                        f();
                }
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
int main(int argc, char const* argv[])
{
        C1 c1;
 
        c1.x();
        c1.f();
 
        return 0;
}

コンパイル

g++  extends_0.cpp -o extends_0

実行例

クラス Bのx()は、f()を呼び出しています。 Bを継承したC1は、f()をオーバーライドしています。

C1のインスタンスc1のxを呼び出すと、基底クラスのf()を呼び出しています。 c1のf()を呼び出すとC1のf()が呼び出されます。

このように、メンバ関数をオーバーライドしても親クラスの関数に影響していません。

% ./extends_0
void B::f()
void C1::f()


仮想関数の例

親クラスの関数が呼び出している関数をオーバーライドしても、親クラスの関数を呼び出すとオーバーライドした関数を呼び出せませんでした。 一部の関数の機能変更をしたいときに、これでは、たくさん書き換えないといけなくなってしまいます。

そこで、継承してオーバーライドして拡張しやすくするには、基底クラスのメンバ関数を仮想関数にします。

ソースコード virtual_2.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                void x() {
                        f();
                }
                virtual void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        C1 c1;
 
        c1.x();
 
        return 0;
}

コンパイル

g++  virtual_2.cpp -o virtual_2

実行例

B の仮想関数 f()は、C1 でオーバーライドしています。 C1 は、基底クラスの x() を呼び出すと、 x()は、C1 でオーバーライドした関数を呼び出しました。

% ./virtual_2
virtual void C1::f()



通常の継承の例

この例では、仮想関数を使用していません。

ソースコード extends_1.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B b;
        C1 c1;
 
        B *p;
 
        p = &b;
        p->f();
        p = &c1;
        p->f();
 
        return 0;
}

コンパイル

g++  extends_1.cpp -o extends_1

実行例

クラス B と C1 は、両方とも基底クラスのf()を呼び出しています。

% ./extends_1
void B::f()
void B::f()

仮想関数の例

ソースコード virtual_1.cpp

#include <iostream>
using namespace std;
 
class B {
        public:
                virtual void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
 
class C1 : public B {
        public:
                void f() {
                        cout << __PRETTY_FUNCTION__ << endl;
                }
};
int main(int argc, char const* argv[])
{
        B b;
        C1 c1;
 
        B *p;
 
        p = &b;
        p->f();
        p = &c1;
        p->f();
 
        return 0;
}

コンパイル

g++  virtual_1.cpp -o virtual_1

実行例

クラス B のポインタにC1のアドレスを渡して、f() を呼び出すと、C1のメンバ関数が呼び出されています。

% ./virtual_1
virtual void B::f()
virtual void C1::f()

関連項目