C++のvirtual関数(コンパイラの観点から)
4488 ワード
いくつかの小さなプログラムを編んでテストして、考えを整理して、以下のようにまとめました.
(ゼロ)VTABLEメカニズム
クラスにvirtual関数がある場合、コンパイラはこのクラスのVTABLEを1つだけ確立します.
このVTABLEは配列のような感じで、その要素は「関数ポインタ」です.
次のコードは
コンパイラはクラスAとクラスBにそれぞれVTABLEを作成します.
VTABLE_Aの0,1,2個の要素はそれぞれA::f 0(),A::f 1(),A::f 2()を指すポインタである.
VTABLE_Bの0,1,2個の要素はそれぞれB::f 0(),B::f 1(),B::f 2()を指すポインタである
実際のクラスオブジェクトごとにvptrフィールドが追加されます
(注:これも多くの筆経で言うsizeof(A)とsizeof(B)に4が多い問題の原因で、この4つのByteがvptrポインタに割り当てられた空間であり、これによって引き起こされたsizeof整列問題がよく聞かれる)
次のコードは次のとおりです.
このように見ると、以前はハードバックの詳細を死記していたことが多く、どこがoverrideなのか、どこがoverloadなのか、呼び出したのはいったいどの関数なのか、vptrとVTABLEの概念でよく理解されていました
(一)多層クラスにおけるvirtrual
下のコードを見てください.
A::f()はvirtualですが、B::f()とC::f()はvirtualを明示的に宣言していません.
実際、C::f()は多態ですA::f()です.
簡単に言えば、コンパイラはAにVTABLEを構築し、Aの派生類ごとにVTABLEを構築する(孫世代のCでも)
一方、A,B,CクラスのVTABLEの要素の順序は同じである(この例では1つのf()しかなく、Aにvirtualのf 2,f 3があれば、BとCのVTABLEの対応する位置にもB,Cバージョンのf 2,f 3が格納される)
(二)virtual構造関数
基本的には、C++の多態は、すべて「虚」で構造関数を分析しなければならない(この1年間、C#が多く使われ、人が馬鹿になった.先日、電面の時、面接官が私と多態を話してくれたが、「虚析構」という場所は軽蔑された=.=)
このように、C++の中で多態を使って、あなたのプログラムがとても土っぽくて、newが空間を積み上げたことがない限り、さもなくば“虚析構造”は必ず使います.
また、コンストラクション関数は虚しくできません.
(三)private虚関数
(四)コンストラクション関数とコンストラクション関数でvirtual関数を呼び出す
変換元:http://www.cnblogs.com/fte99923/archive/2011/04/24/2026517.html
(ゼロ)VTABLEメカニズム
クラスにvirtual関数がある場合、コンパイラはこのクラスのVTABLEを1つだけ確立します.
このVTABLEは配列のような感じで、その要素は「関数ポインタ」です.
次のコードは
1 class A
2 {
3 public:
4 virtual void f0() {cout<<"a0"<<endl};
5
6 virtual void f1() {cout<<"a1"<<endl};
7
8 virtual void f2() {cout<<"a2"<<endl};
9
10 };
11
12 class B: public A
13 {
14 public:
15 virtual void f0() {cout<<"b0"<<endl};
16
17 virtual void f1() {cout<<"b1"<<endl};
18
19 virtual void f2() {cout<<"b2"<<endl};
20
21 };
コンパイラはクラスAとクラスBにそれぞれVTABLEを作成します.
VTABLE_Aの0,1,2個の要素はそれぞれA::f 0(),A::f 1(),A::f 2()を指すポインタである.
VTABLE_Bの0,1,2個の要素はそれぞれB::f 0(),B::f 1(),B::f 2()を指すポインタである
実際のクラスオブジェクトごとにvptrフィールドが追加されます
(注:これも多くの筆経で言うsizeof(A)とsizeof(B)に4が多い問題の原因で、この4つのByteがvptrポインタに割り当てられた空間であり、これによって引き起こされたsizeof整列問題がよく聞かれる)
次のコードは次のとおりです.
A *a = new B;
a->f1(); // , a->vptr[1](); , VTABLE_B[1]
このように見ると、以前はハードバックの詳細を死記していたことが多く、どこがoverrideなのか、どこがoverloadなのか、呼び出したのはいったいどの関数なのか、vptrとVTABLEの概念でよく理解されていました
(一)多層クラスにおけるvirtrual
下のコードを見てください.
class A
{
public:
virtual void f(){cout<<"a"<<endl;}
void g(){cout<<"ag"<<endl;}
};
class B: public A
{
public:
void f(){cout<<"b"<<endl;} // B::f() virtual !
};
class C: public B // C B, B virtual
{
public:
void f(){cout<<"c"<<endl;} // C f virtual !
void g(){cout<<"cg"<<endl;}
};
void main()
{
A *a = new C();
a->f(); // C::f(), a->vptr[0]();
a->g(); // A::g(), g A virtual
delete a;
a = new B();
a->f(); // ( , ) B::f(),a->vptr[0]();
delete a;
}
A::f()はvirtualですが、B::f()とC::f()はvirtualを明示的に宣言していません.
実際、C::f()は多態ですA::f()です.
簡単に言えば、コンパイラはAにVTABLEを構築し、Aの派生類ごとにVTABLEを構築する(孫世代のCでも)
一方、A,B,CクラスのVTABLEの要素の順序は同じである(この例では1つのf()しかなく、Aにvirtualのf 2,f 3があれば、BとCのVTABLEの対応する位置にもB,Cバージョンのf 2,f 3が格納される)
(二)virtual構造関数
基本的には、C++の多態は、すべて「虚」で構造関数を分析しなければならない(この1年間、C#が多く使われ、人が馬鹿になった.先日、電面の時、面接官が私と多態を話してくれたが、「虚析構」という場所は軽蔑された=.=)
1 class A
2 {
3 public:
4 A() { cout<<"A()"<<endl;ptra_ = new char[10];}
5 virtual ~A() { cout<<"~A()"<<endl; delete[] ptra_;} // : “ ” ,main A(),B(),~A()
6
7 // “ ” ,main A(),B(),~B(),~A()
8
9 private:
10 char * ptra_;
11 };
12
13 class B: public A
14 {
15 public:
16 B() { cout<<"B()"<<endl;ptrb_ = new char[20];}
17 ~B() { cout<<"~B()"<<endl; delete[] ptrb_;}
18 private:
19 char * ptrb_;
20 };
21
22 void main()
23 {
24 A * a = new B;
25 delete a;
26 }
このように、C++の中で多態を使って、あなたのプログラムがとても土っぽくて、newが空間を積み上げたことがない限り、さもなくば“虚析構造”は必ず使います.
また、コンストラクション関数は虚しくできません.
(三)private虚関数
class A
{
public:
void foo() { bar();} // this->vptr[0], , “this”
private:
virtual void bar() {cout<<"a"<<endl;}
};
class B: public A
{
private:
virtual void bar() { cout<<"b"<<endl;}
};
void main()
{
A *a = new B();
a->foo(); //a B, a->vptr[0] VTABLE_B[0] , B::bar()
}
(四)コンストラクション関数とコンストラクション関数でvirtual関数を呼び出す
class A
{
public:
A() { cout<<"A()"<<endl;foo();} // , A::foo() !
~A() { cout<<"~A()"<<endl;foo();} //
virtual void foo(){cout<<"a"<<endl;}
};
class B: public A
{
public:
virtual void foo(){cout<<"b"<<endl;}
};
class C: public B
{
public:
virtual void foo(){cout<<"c"<<endl;}
};
void main()
{
A *a = new B;
delete a;
B *b = new C;
delete b;
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
// 2 、2 a::foo()
// , ,B vptr ; ,vptr , B::vptr B VTABLE
}
変換元:http://www.cnblogs.com/fte99923/archive/2011/04/24/2026517.html