C++中的继承问题

来源:互联网 发布:java 网页爬虫框架 编辑:程序博客网 时间:2024/05/29 19:11

1、c++继承关于子类的默认函数的写法

如果子类没有定义构造方法,则调用父类的无参数的构造方法

如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类的构造方法,然后在执行自己的构造方法

在创建子类对象的时候,如果子类的构造函数没有显示调用父类的构造函数,则会调用父类的默认无参数构造函数

在创建子类对象的时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。

如果子类调用父类带参数的构造方法,则需要父类的构造函数

赋值兼容(前提:public 继承权限)

1、派生类对象可以直接赋值给基类对象

2、基类对象的指针或引用可以指向派生类对象

同名隐藏

在继承体系中基类和派生类都有自己独立的作用域。

子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问,

(在子类当中可以使用基类::基类成员访问),隐藏重定义。

在实际中在继承体系当中最好不要用同名成员

与同名隐藏相像的就是钻石继承问题,要区分开。同名隐藏是在基类和派生类里有相同的成员变量或者是成员函数。而钻石继承是相对于最后一个派生类而言,它的两个基类里有相同的成员函数或成员变量。

继承 

定义:继承是面向对象复用的重要手段,通过继承定义一个类,继承是类型之间的关系模型,共享公有的东西,实现各自本质不同的东西。有三种继承关系:public(公有继承),protect(保护继承),private(私有继承)
                                 
1、基类的private成员在派生类中是不能被访问的,基类的protected成员和public成员可以在派生类内部被访问;
2、public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用
3、protected和private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是has-a原则
4、使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public
继承与转换 赋值兼容原则 public继承
1、子类可以赋值给父类对象
2、父类不可以赋值给子类对象
3、父类指针/引用可以指向子类对象
4、子类指针/ 引用不能指向父类对象

2、分析菱形继承的问题

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

#pragma once#include<iostream>using namespace std;class A{public:    int _a;};class B : public A{public:    int _b;};void Test(){    B b;    b._a = 1;    b._b = 2;}

图解:

                    

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

#pragma once#include<iostream>using namespace std;class A1{public:    int _a1;};class A2{public:    int _a2;};class B : public A1, public A2{public:    int _b;};void Test(){    B b;    b._a1 = 1;    b._a2 = 2;    b._b = 3;}


如图

                          

形继承是建立在多继承的基础上。菱形继承:也称多边形继承

菱形继承:两个子类同时继承一个父类,而又有子类同时继承这两个子类

#pragma once#include<iostream>using namespace std;class A{public:    int _a;};class B :  public A{public:    int _b;};class C :  public A{public:    int _c; };class D :public B, public C{public:    int _d;  };void Test(){    D d;    d.B::_a = 1;//二义性问题的第一种解决方法(域限定符)    d.C::_a = 2;    d._b = 3;    d._c = 4;    d._d = 5;}

程序运行结果

                                    
从内存中看到菱形继承派生而立的对象模型


                             

理解钻石继承

钻石继承也就是菱形继承,如下:
                                                     
#pragma once#include<iostream>using namespace std;class A{public:    int _a;};class B : public A{public:    int _b;};class C : public A{public:    int _c; };class D :public B, public C{public:    int _d;  };void Test(){    D d;    d._a = 1;}

     
              
继承结构中,B类和C类都继承自A基类。所以D类多重继承了B类和C类,因此D类会有两份A基类的成员,将会导致编译错误,因为编译器不知道是调用B类成员的_a还是调用C类的成员_a,所以调用成员变量方法是不明确的,因此不能通过编译,这就是菱形继承存在的二义性和数据冗余的问题


刚才我们用了域访问限定符来解决编译器无法识别是对B对象还是对C对象的_a赋值的问题,这样虽然解决的二义性问题,,但没有解决数据冗余的部分


3、分析虚拟继承是怎么解决二义性和数据冗余的

虚拟继承是一种机制,类通过虚拟继承指出它希望共享虚基类的状态。对给定的虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类对象,共享基类子对象称为虚基类。虚基类用virtual声明继承关系,这样D就只有A的一份拷贝。

如果B类和C类对象在分别继承A类时都用virtual来标注,对于D类对象,C++会保证只有一个A的子类的字对象会被创建。

#pragma once#include<iostream>using namespace std;class A{public:    int _a;};class B :  virtual public A{public:    int _b;};class C : virtual public A{public:    int _c; };class D :public B, public C{public:    int _d;  };void Test(){    D d;    d._a = 1;    d._a = 2;    d._b = 3;    d._c = 4;    d._d = 5;}


       

         

通过监视窗口看到对_a赋值B类和C类的_a都会被改变说明在D类中的对象模型中只存在一个_a,这样即解决了二义性问题又解决了数据冗余的问题。


菱形虚拟继承的实现原理

虚拟继承后D派生类的大小比之前菱形继承后D派生类的内存大了


                    


                 


            

从内存中我们得到

                 

发现内存里多了偏移量地址 


虚继承解决了在菱形继承中子类对象包含多份父类对象的数据冗余问题,菱形继承解决了数据冗余的问题的同时也带来的性能上的损耗

一个类如何才能不被继承

一个类不想被继承,也就是说它的子类不能构造父类,可以将一个子类声明为私有的,使这个类的构造函数对子类不可见,那这个类就不能被继承了。但是这样做有一个缺点,加入现在只有A类,在A类外无法实例化对象。为了让A类不被继承还能够正常使用,我们可以将A类虚继承E类,但E类的构造函数是带private属性的,A类还是E类的友元。

原创粉丝点击