C++继承概念——权限,对象模型,菱形虚拟继承

什么是继承?

继承是面向对象设计的重要部分,允许在原有类特性上进行扩展和代码复用!

符合认知上的从浅入深的过程。

访问权限和继承权限

访问权限是指对于本类或者对象的成员来说的,继承权限是在访问权限的基础上谈的,这一点很容易理不清。

访问权限

private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.

protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问

public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问

其中protected的作用是不想让成员在类外被使用,但是可以让子类使用,因此可以说在继承中才能体现其作用。

继承权限

因为派生类完全继承了基类的成员变量,所以现在讨论继承过来的成员的权限问题。

public:
public和protected成员权限在派生类没有改变,private在派生类不可见
protected:
public降级为protected, protected不变,private不可见
private:
public降级为private,protected降级为private,private不可见

如何证明子类继承了一个protected权限的成员?

分别在子类内部访问成员,在类外用对象访问成员。

其中public继承最为常用,每个父类成员对子类也可用, 子类 IS-A 父类。

class和struct的默认继承权限分别是private和public。

派生类默认的成员函数

与一般类一样,在需要时编译器合成相应的六个成员函数。

  1. 基类没有定义构造函数或者有缺省构造,则派生类不需要显式定义。
class Date {
public:
    //缺省构造
    Date()
    {
        _day = 1;
    }
    Date(int)
    {
        _day = 2;
    }
private:
    int _day;
};

class Time :public Date
{
public:
    //假如不写,编译器也会合成默认构造
    Time()
    :Date()//隐式调用
    {
    }
private:
    int _time;
};
  1. 如果基类只有带参构造函数,则派生类必须在构造函数参数列表调用基类构造函数。
class Date {
public:
    Date(int)
    {
        _day = 2;
    }
private:
    int _day;
};

class Time :public Date
{
public:
    Time()
        :Date(1)
    {
    }
private:
    int _time;
};

构造函数的调用顺序

  1. 基类构造函数
  2. 成员类对象构造函数(多个按声明顺序)
  3. 派生类构造函数

赋值兼容规则

由于构造函数是先父后子,自然在内存中的模型就是先放父成员后放子成员

复制兼容原则是大的可以赋给小的,小权限指针可以指向大权限指针。

翻译一下就是派生类可以切割给基类对象赋值,父类指针可以限制的指向子类。

基类那些成员被派生继承了?友元可以继承吗?

私有成员也被继承,由于C++语法的限制,在派生类中访问基类的私有成员只能通过函数访问。

static成员因为在静态区,父类的static变量和函数在派生类中依然可用,但是受访问性控制。

友元关系:父亲的朋友不是儿子的朋友。

菱形继承和虚拟继承

菱形继承的模型:基类A,派生类B1,B2都继承A,C继承B1和B2.

缺陷:C类里有两份A类成员,分别从B1B2继承来的,存在二义性,必须用类限定符访问

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 {
public:
    int _d;
};

int main()
{
    cout << sizeof(C1) << endl;
    cout << sizeof(D) << endl;
    C1 c;
    c._b = 5;
    c._c1 = 6;
    D d;
    d._b = 1;
    d._c1 = 2;
    d._c2 = 3;
    d._d = 4;
    system("pause");
}

虚拟继承——声明virtual

0x004FFD54  30 7b 93 00  
0x004FFD58  06 00 00 00  
0x004FFD5C  05 00 00 00

通过C1的对象模型可以看到前4字节存放一个指针,然后先放派生类成员再放基类成员。

虚拟菱形继承

0x004FFD34  e0 7b 93 00  ?{?.
0x004FFD38  02 00 00 00  ....
0x004FFD3C  e8 7b 93 00  ?{?.
0x004FFD40  03 00 00 00  ....
0x004FFD44  04 00 00 00  ....
0x004FFD48  01 00 00 00

虚拟菱形继承D的模型,先按继承顺序存放C1,C2,再放本类,最后放基类成员。

虚拟菱形继承实现了基类成员仅存放一份,消除二义性。

怎么实现的?

虚拟继承的派生类对象前四字节存放指针,指针指出了派生类相对基类的偏移量,从而两个派生类相对于基类成员有了不同的偏移,可以保证访问到同一个基类对象。这里的偏移量是在构造函数里填充的,编译器自动合成默认构造。

猜你喜欢

转载自blog.csdn.net/hanzheng6602/article/details/80872435