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

【C++深度解析】39、被遗弃的多重继承(下)

程序员文章站 2022-05-29 18:18:07
...

1 多重继承问题三:产生多个虚函数表

【C++深度解析】39、被遗弃的多重继承(下)
继承是父类与子类的叠加,BaseA 和 BaseB 中都有虚函数,那么二者都会有虚函数表,Dervied 继承了这两个类,类中也就有了两个指针,分别指向两个虚函数表。

编程实验:多重继承问题–产生多个虚函数表

// 38-3.cpp
#include<iostream>
using namespace std;
class BaseA
{
public:
    virtual void funcA()
    {
        cout << "BaseA::funcA()" << endl;
    }
};
class BaseB
{
public:
    virtual void funcB()
    {
        cout << "BaseB::funcB()" << endl;
    }
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
    Derived d;
    BaseA* pa = &d;
    BaseB* pb = &d;
    BaseB* pbb = (BaseB*)pa;
    BaseB* pbc = dynamic_cast<BaseB*>(pa);
    cout << "sizeof(b) = " << sizeof(d) << endl;
    pa->funcA();
    pb->funcB();
    pbb->funcB();
    cout << endl;
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pbb = " << pbb << endl;
    cout << "pbc = " << pbc << endl;
    return 0;
}
  • BaseA,BaseB 中有虚函数,Derived 继承了这两个类,所有就有两个虚函数表,也就有两个指针分别指向这两个虚函数表。所以 Derived 大小为 16。
  • 子类的引用可以转换为父类的指针,根据多态,pa 可以调用类 BaseA 中的函数 funcA(),pb 可以调用类 BaseB 中的函数 funcB()
  • 第 28 行,将BaseA* 类型的指针转换为 BaseB* 类型的指针,会调用什么函数呢?
  • 使用新式类型转换 dynamic_cast 再尝试,最后我们打印四个指针的值。

编译运行:

$ g++ 38-3.cpp -o 38-3
$ ./38-3
sizeof(b) = 16
BaseA::funcA()
BaseB::funcB()
BaseA::funcA()

pa = 0x7ffc0d30ac40
pb = 0x7ffc0d30ac48
pbb = 0x7ffc0d30ac40
pbc = 0x7ffc0d30ac48

类大小为 16,说明子类有两个虚函数表,两个指针。
pbb 是 BaseB* 类型的指针,但是调用的确是类 BaseA 中的函数。
从最后打印的指针数值上可以看到 pa,pbb 都是指向类 BaseA,pb 和 pbc 指向类 BaseB。

为什么 BaseB* 类型的指针 pbb 会指向类 BaseA 呢?如下图所示:
【C++深度解析】39、被遗弃的多重继承(下)
直接强制类型转换,将 BaseA* 类型的指针转换为 BaseB* 类型的指针,指向的仍然是 BaseA 的成员函数。并不能调用 BaseB 的成员函数。

1.1 解决方案:dynamic_cast

想要完成指针类型转换的同时改变指向的函数,需要使用 dynamic_cast,自动帮我们完成转换

  • 用于有继承关系的类指针间的转换
  • 用于有交叉关系的类指针间的转换
  • 具有类型检查功能
  • 需要虚函数的支持

2 正确使用多重继承

单继承某个类+实现(多个)接口

【C++深度解析】39、被遗弃的多重继承(下)

// 38-4.cpp
#include<iostream>
using namespace std;
class Base
{
protected:
    int mi;
public:
    Base(int i) { mi = i; }
    int getI()  { return mi; }
    bool equal(Base* obj)
    {
        return this == obj;
    }
};

class Interface1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};
class Interface2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};
class Derived : public Base, public Interface1, public Interface2
{
public:
    Derived(int i) : Base(i) {}
    void add(int i)
    {
        mi += i;
    }
    void minus(int i)
    {
        mi -= i;
    }
    void multiply(int i)
    {
        mi *= i;
    }
    void divide(int i)
    {
        if (i != 0)
        {
            mi /= i;
        }
    }
};
int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    cout << "p->getI() = " << p->getI() << endl;
    pInt1->add(10);
    pInt2->divide(11);
    pInt1->minus(5);
    pInt2->multiply(8);
    cout << "p->getI() = " << p->getI() << endl;
    cout << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    return 0;
}
  • Derived 有三个父类,其中一个是类Base,另外两个是接口,避免了数据冗余的问题。
  • 在子类中要实现 equal() 判断指针是否指向当前对象。
  • 将一个父类指针转换为另一个父类,使用 dynamic_cast
$ g++  38-4.cpp -o 38-4
$ ./38-4
p->getI() = 100
p->getI() = 40

pInt1 == p : 1
pInt1 == p : 1

工程建议:

  • 先继承一个类,然后实现第一个接口
  • 父类中提供 equal() 成员函数,判断指针是否指向当前对象
  • 多重继承相关强制类型转换用 dynamic_cast 完成

3 小结

1、多继承可能出现多个虚函数表指针
2、与多重继承相关强制类型转换用 dynamic_cast 完成
3、工程中采用“单继承多接口”的方式使用多继承
4、父类提成成员函数用于判断指针是否指向当前对象

相关标签: C++深度解析