C++多态深度解析

一.多态的概念

通过使用virtual关键字对多态进行支持,被virtual声明的函数被重写后具有多态特性,virtual声明的函数叫虚函数。

复制代码
 1 #include <iostream>
 2 #include <string>
 3  
 4 using namespace std;
 5  
 6 class Parent
 7 {
 8 public:
 9     virtual void print() //使用virtual,变成虚函数(这个函数可能在子类被重写)
10     {
11         cout << "I'm Parent." << endl;
12     }
13 };
14  
15 class Child : public Parent
16 {
17 public:
18     void print()  //由于继承关系,这里也是虚函数,只是可以省略不写
19     {
20         cout << "I'm Child." << endl;
21     }
22 };
23  
24 void how_to_print(Parent* p)
25 {
26     p->print();     // 展现多态的行为
27 }
28  
29 int main()
30 {
31     Parent p;
32     Child c;
33     
34     how_to_print(&p);    // Expected to print: I'm Parent.
35     how_to_print(&c);    // Expected to print: I'm Child.
36     
37     return 0;
38 }
复制代码

结果:

1 I'm Parent.
2 I'm Child.    

 子类中重写的函数将覆盖父类中的函数,虽然被重写覆盖了,但通过作用域分辨符(::)可以访问到父类中的函数。

二.虚函数与纯虚函数

纯虚函数和虚函数都是使用virtual关键字,只是纯虚函数在函数原型后面加上“=0”,纯虚函数没有实例化,虚函数有函数体,有实例化,定义纯虚函数是为了实现一个接口,起到一个规范的作用。

复制代码
 1 #include <iostream>
 2  
 3 using namespace std;
 4  
 5 class A{
 6 public:
 7   virtual void f(const string& str)=0;
 8   virtual void e() {
 9     cout << "Base e() : " << endl;
10   }
11 };
12 // 缺省实现,纯虚函数可以有函数体,但必须类的外部定义,如果子类不想实现,可以调用基类的
13 void A::f(const string& str) {
14    cout << "A f()" << str << endl;
15 }
16  
17 class B : public A{
18 public:
19 // 本质上还是重写了,只不过直接调用了基类的函数实现
20   virtual void f(const string& str){
21     cout << "Call A::f " << endl;
22     A::f(str);
23   } 
24   void e(){
25     cout << "B e()" << endl;
26   }
27 };
28  
29 int main()
30 {
31   B b;
32   b.f("hello world");
33   b.e();
34   A *a = new B();
35   // 都是多态
36   a->f("hello");
37   a->e();
38   return 0;
39 }
复制代码

三.对象的内存布局

对象的内存布局包括两方面:成员变量的内存,指向虚函数表指针的内存,静态变量与成员函数是不占用对象内存的。

空类的大小为:1

复制代码
 1 #include <iostream>
 2 using namespace std;
 3  
 4 class NoMembers
 5 {
 6 };
 7  
 8 int main()
 9 {
10     NoMembers n;  // Object of type NoMembers.
11     cout << "The size of an object of empty class is: "
12          << sizeof(n) << endl;
13 }
复制代码

含有虚函数成员的类大小为4:含有虚函数的类需要维护一个虚函数表,有一个指向虚函数表的指针.

复制代码
 1 class Base {
 2  
 3 public:
 4  
 5 virtual void f() { cout << "Base::f" << endl; }
 6  
 7 virtual void g() { cout << "Base::g" << endl; }
 8  
 9 virtual void h() { cout << "Base::h" << endl; }
10  
11 };
复制代码

多继承下类的大小

复制代码
 1 #include<iostream>
 2 using namespace std;
 3  
 4  
 5 class A     
 6 {     
 7 };    
 8  
 9 class B     
10 {  
11     char ch;     
12     virtual void func0()  {  }   
13 };   
14  
15 class C    
16 {  
17     char ch1;  
18     char ch2;  
19     virtual void func()  {  }    
20     virtual void func1()  {  }   
21 };  
22  
23 class D: public A, public C  
24 {     
25     int d;     
26     virtual void func()  {  }   
27     virtual void func1()  {  }  
28 };     
29 class E: public B, public C  
30 {     
31     int e;     
32     virtual void func0()  {  }   
33     virtual void func1()  {  }  
34 };  
35  
36 int main(void)  
37 {  
38     cout<<"A="<<sizeof(A)<<endl;  //1   
39     cout<<"B="<<sizeof(B)<<endl;  //4+4=8        
40     cout<<"C="<<sizeof(C)<<endl;  //4+4=8    
41     cout<<"D="<<sizeof(D)<<endl;  //8+4=12   
42     cout<<"E="<<sizeof(E)<<endl;  //8+4=12    
43     return 0;  
44 }  
复制代码

虚继承的情况

复制代码
 1 class A {
 2  
 3     int a;
 4  
 5 };
 6  
 7 class B:virtual public A{
 8  
 9     virtual void myfunB(){}
10  
11 };
12  
13 class C:virtual public A{
14  
15     virtual void myfunC(){}
16  
17 };
18  
19 class D:public B,public C{
20  
21     virtual void myfunD(){}
22  
23 };
复制代码

以上代码中sizeof(A)=4,,sizeof(B)=12,sizeof(C)=12,sizeof(D)=16。

B,C中由于是虚继承因此大小为int大小,加指向虚基类的指针的大小,加指向虚函数表指针的大小。

四.class退化成了struct,他们内存布局一样

复制代码
 1 #include <iostream>
 2 #include <string>
 3  
 4 using namespace std;
 5  
 6 class A       //大小
 7 {
 8     int i;    //4
 9     int j;    //4
10     char c;   //1 + 3(补3)
11     double d; //8
12 public:
13     void print()
14     {
15         cout << "i = " << i << ", "
16              << "j = " << j << ", "
17              << "c = " << c << ", "
18              << "d = " << d << endl;
19     }
20 };
21  
22 struct B       //大小
23 {
24     int i;     //4
25     int j;     //4
26     char c;    //1 + 3(补3)
27     double d;  //8
28 };
29  
30 int main()
31 {
32     A a;
33     
34     cout << "sizeof(A) = " << sizeof(A) << endl;    // 20 bytes
35     cout << "sizeof(a) = " << sizeof(a) << endl;
36     cout << "sizeof(B) = " << sizeof(B) << endl;    // 20 bytes
37     
38     a.print();
39     //p指向了a的地址
40     B* p = reinterpret_cast<B*>(&a);    //a不是整数。&a变成地址后进行类型转换
41     /*
42     reinterpret_cast强制类型转换 
43     用于指针类型间的强制转换
44        用于整数和指针类型间的强制转换(类指针也是指针)
45     */
46     
47     p->i = 1;
48     p->j = 2;
49     p->c = 'c';
50     p->d = 3;
51     
52     a.print();
53     
54     p->i = 100;
55     p->j = 200;
56     p->c = 'C';
57     p->d = 3.14;
58     
59     a.print();
60     
61     return 0;
62 }
复制代码

五.多继承方式会产生多个虚函数表

复制代码
 1 #include <iostream>
 2 #include <string>
 3  
 4 using namespace std;
 5  
 6 class BaseA
 7 {
 8 public:
 9     virtual void funcA()
10     {
11         cout << "BaseA::funcA()" << endl;
12     }
13 };
14  
15 class BaseB
16 {
17 public:
18     virtual void funcB()
19     {
20         cout << "BaseB::funcB()" << endl;
21     }
22 };
23  
24 class Derived : public BaseA, public BaseB
25 {
26  
27 };
28  
29 int main()
30 {
31     Derived d;
32     BaseA* pa = &d;    //pa->BaseA
33     BaseB* pb = &d;    //pb->BaseB
34     BaseB* pbe = (BaseB*)pa;    // oops!!指向了A的虚函数表(重点在这里)
35     BaseB* pbc = dynamic_cast<BaseB*>(pa);    //指向了B的虚函数表
36     
37     cout << "sizeof(d) = " << sizeof(d) << endl; //8,两个指针,指向虚函数表
38     
39     cout << "Using pa to call funcA()..." << endl;
40     
41     pa->funcA();    //BaseA::funcA()
42     
43     cout << "Using pb to call funcB()..." << endl;
44     
45     pb->funcB();    //BaseB::funcB()
46     
47     cout << "Using pbc to call funcB()..." << endl;
48     
49     pbc->funcB();   //BaseB::funcB()
50     
51     cout << endl;
52     
53     cout << "pa = " << pa << endl;    //假设:A
54     cout << "pb = " << pb << endl;    //假设:B
55     cout << "pbe = " << pbe << endl;    
56     cout << "pbc = " << pbc << endl;
57     
58     return 0;
59 }
复制代码

结果:

复制代码
 1 sizeof(d) = 8
 2 Using pa to call funcA()...
 3 BaseA::funcA()
 4 Using pb to call funcB()...
 5 BaseB::funcB()
 6 Using pbc to call funcB()...
 7 BaseB::funcB()
 8  
 9 pa = 0xbf9d2558
10 pb = 0xbf9d255c
11 pbe = 0xbf9d2558
12 pbc = 0xbf9d255c
复制代码

强制转换后(Base* pbb = (Base*)pa),pbb是BaseB类型,应该指向vptr2的虚函数表,但却指向了vptr1的虚函数表 。

这时需要进行强制类型转换时,C++中推荐使用新式类型转换关键字(dynamic_cast)!!,BaseB* pbc = dynamic_cast<BaseB*>(pa),指向了B的虚函数表。

posted @   学习&笔记  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示