欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++从入门到放弃之:多重继承、钻石继承、虚继承

程序员文章站 2022-07-15 16:50:08
...

1. 多重继承(multiple inheritance)

  • 概念
    一个子类可以同时继承多个基类,这样的继承方式称为多重继承

  • 向上造型指针偏移问题
    向上造型时,编译器会根据各个基类子对象的内存布局,进行适当地偏移计算,以保证指针的类型和所指向的基类子对象类型一致

  • 名字冲突问题
    1> 如果子类继承的多个基类中存在相同的名字,当通过子类访问这些名字时,编译器会报歧义错误_名字冲突
    2> 解决名字冲突的通用做法是显示的使用类名::作用域限定符的方式来指明所访问的名字属于哪个基类
    3> 如果冲突的名字是成员函数,并且参数不同,也可以通过using声明将他们引入到子类的作用域,让其在子类中构成重载关系,通过函数重载匹配来解决名字冲突问题

#include <iostream>

using namespace std;

class Base1 {
public:
    void func(void){
        cout<<"func1"<<endl;
    }
    int m_i;
};
class Base2{
public:
    void func(int i){
        cout <<"func2"<<endl;
    }

    typedef int m_i;
};
class Derived:public Base1, public Base2{
public:
    //解决方法二:将基类的两个函数都声明到当前子类,从而使两个函数构成重载关系
    using Base1::func;
    using Base2::func;

};
int main() {
    Derived d;
    //d.func();
    //d.func(123);//error: request for member ‘func’ is ambiguous
    /* 解决方法一:添加作用域限定符,显式指明调用的函数属于哪个基类
    d.Base1::func();
    d.Base2::func(123);
    */
    d.func();
    d.func(123);
    d.Base1::m_i = 100;
    Derived::Base2::m_i i = 200;
    cout<<d.Base1::m_i<<endl;
    cout<<i<<endl;
    return 0;
}

2. 钻石继承

  • 一个子类的多个基类源自于共同的基类祖先,这样的继承结构被称为钻石继承
    钻石继承结构:
    公共基类
    /
    自己的公共基类<- 中间类 中间类 ->自己的公共基类
    \ /
    末端子类(包含两公共基类的子对象)
  • 在创建末端子类对象时会包含多份公共基类子对象,通过末端子类访问公共基类成员时会因为继承路径不同导致结果不一致

3. 虚继承

1> 虚继承作用
通过虚继承可以让公共基类子对象在末端子类对象中实例唯一,并可以为所有的中间类共享,在末端子类访问公共基类成员,即便沿着不同的继承路径,所访问到的基类成员也是一致的
虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。
2>虚继承语法
.在继承表中使用virtual关键字修饰(定义中间类时)
.位于继承链的末端子类负责构造公共基类子对象
3>虚继承实现的原理
虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
虚表指针->
-/虚基类表(保存了当前中间类相对于公共基类的偏移量)
-\虚函数表(函数指针数组,每个函数指针保存了虚函数在代码段的位置)

#include <iostream> 

using namespace std;

class A {
public:
    A(int data) : m_data(data) {

    }

protected:
    int m_data;
};

class B : virtual public A {
public:
    B(int data) : A(data) {}

    void set(int data) {
        m_data = data;
    }

};

class C : virtual public A {
public:
    C(int data) : A(data) {

    }

    int get() {
        return m_data;
    }
};

class D : public B, public C {
public:
    //虚继承时,由末端子类负责构造公共基类子对象
    D(int data) : B(data), C(data),A(data) {}
};

int main() {
    D d(100);
    cout << d.get() << endl;//100
    d.set(200);
    cout << d.get() << endl;

    return 0;
}
/home/panda/WorkSpace/CPP/cmake-build-debug/CPP
100
200

进程已结束,退出代码 0

相关标签: C++