继承小结

来源:互联网 发布:店淘精品数据采集神器 编辑:程序博客网 时间:2024/05/18 19:44

 

     继承是复用代码的重要手段也为动态多态提供了平台

     定义格式:

                  

一、 继承关系

    基类(父类)   

    派生类(子类)

   派生类对象模型如图:

                                              

    


     

   类 缺少继承权限:默认权限为private继承

   结构体缺少继承权限:默认权限为public继承

   

  

#include <iostream>using namespace std;class Base{public:Base(int a = 1):_b(a){}~Base(){cout << "Base()" << endl;}int _B;private:int _b;};class Derive :public Base{public:Derive(int a):Base(a),_d(a){}~Derive(){cout << "Derive()" << endl;}private:int _d;};class D :protected Base{public:D(int a):Base(a), _D(a){}~D(){cout << "D()" << endl;}private:int _D;};class DD :private Base{public:DD(int a):Base(a), _DD(a){}~DD(){cout << "DD()" << endl;}private:int _DD;};int main(){Derive a(1);D b(1);DD c(1);/*Base b(1);*/a._B = 10;b._B = 1;c._B = 1;system("pause");return 0;}

   二、

          派生类构造函数的初始化链表先完成基类对象构造(调用基类的构造函数)最后构造派生类自己特有的成员

                派生类先调用自己的析构函数最后调用基类析构函数

         1、基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
         2、基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
         3、基类定义了带有形参表构造函数,派生类就一定定义构造函数。

 否则:出现如下错误        

#include <iostream>using namespace std;class Base{public:Base(int a ):_b(a){}~Base(){cout << "Base()" << endl;}int _B;private:int _b;};class B{private:int _B1;};class Derive :public Base{public:Derive(int a):Base(a),_d(a){}~Derive(){cout << "Derive()" << endl;}private:int _d;};class D :public Base{public:D(int a): _D(a){}~D(){cout << "D()" << endl;}private:int _D;};class DD :public B{public:~DD(){cout << "DD()" << endl;}private:int _DD;};int main(){Derive a(1);D b1;D b(1);DD c;/*Base b(1);*//*a._B = 10;b._B = 1;c._B = 1;*/a.~Derive();system("pause");return 0;}


三、继承体系中的作用域

1. 在继承体系中基类和派生类是两个不同作用域。(不能构成重载)
2. 子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以
使用 基类::基类成员 访问)--隐藏 --重定义
3. 注意在实际中在继承体系里面最好不要定义同名的成员。

#include <iostream>using namespace std;class Base{public:Base(int a = 1):_b(a){}void FunTest(int a){ }~Base(){cout << "Base()" << endl;}int _B;private:int _b;};class Derive :public Base{public:Derive(int a):Base(a),_d(a){}void FunTest(){ }~Derive(){cout << "Derive()" << endl;}int _B;private:int _d;};int main(){Derive a(1);Base B;/*D b1;D b(1);DD c;*//*Base b(1);*//*a._B = 10;b._B = 1;c._B = 1;*//*a.FunTest(a);*/// 不能形成重载a._B = 1; // 不能给基类赋值 a.Base::_B = 1; 对基类赋值a.~Derive();system("pause");return 0;}



四、继承与转换--赋值兼容规则--public继承
1. 子类对象可以赋值给父类对象(切割/切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)

    

友元关系不可继承


五、多继承

          

#include <iostream>using namespace std;class B1{public:int _B1;};class B2{public:int _B2;};class D :public B1,public B2  //继承B1和B2 {public:int _D;};int main(){D d;cout <<  sizeof(d) << endl;d._B1 = 0;d._B2 = 1;d._D = 2;system("pause");return 0;}
调试得到:


对象d 的大小为3*4 = 12个字节, 取d地址, 从内存中可以看出 d的对象模型为



六、菱形继承

       如图所示继承:

                      


#include <iostream>using namespace std;class B{public:int _B;};class C1:public B{public:int _c1;};class C2:public B{public:int _c2;};class D :public C1,public C2  //继承B1和B2 {public:int _D;};int main(){D d;cout <<  sizeof(d) << endl;//d._B = 0;   产生二义性问题d.C1::_B = 0;d.C2::_B = 1;d._c1 = 2;d._c2 = 3;d._D = 4;system("pause");return 0;}

调试结果如图


得到对象模型


七、虚继承 

          虚继承关键字 :virtual   

    虚继承 避免了二义性问题

     

#include <iostream>using namespace std;class B{public:int _B;};class C1:virtual public B{public:int _c1;};class C2:virtual public B{public:int _c2;};class D : public C1, public C2  //继承B1和B2 {public:int _D;};int main(){D d;cout <<  sizeof(d) << endl;d._B = 0;  // 产生二义性问题d._c1 = 1;d._c2 = 2;d._D = 3;system("pause");return 0;}

调试:

d的大小为24, 对象模型为:


取d的地址发现前四个字节不是某个类成员变量的值,它其实是个地址 ,在内存2中输入前四个字节的内容发现 它前四个字节为0x00 00 00 00转化为十进制就是0  接着为0x 14 00 00 00 转化为十进制就是20。

这个地址是偏移量表格的地址。

偏移量表格里面放的是 相对于自己的偏移量以及相对于基类的偏移量。

如内存2中存放的内容:

第一个字节放的为0 相对于C1偏移量为0。

第二个字节放的为20 相对于B偏移量为20  即 &d+20 得到存放基类的地址。


欢迎各位,批评指正。

原创粉丝点击