第五十四课、被遗弃的多重继承(下)
一、多继承产生的问题三:可能产生多个虚函数表
#include<iostream> using namespace std; class BaseA { public: virtual void func() { cout << "BaseA::func()" << endl; } }; class BaseB { public: virtual void func() { cout << "BaseB::func()" << endl; } }; class Derived : public BaseA, public BaseB { }; int main() { cout << "sizeof(Derived) = " << sizeof(Derived) << endl;//8==>两个指向虚函数表的指针 Derived d; BaseA *pa = &d; BaseB *pb = &d; pa->func();//BaseA::func() pb->func();//BaseB::func() cout << "pa = " << pa << endl;//pa = 0xbfb3b368 cout << "pb = " << pb << endl;//pb = 0xbfb3b36c, 比pa的地址大4,正好是一个指针的大小 cout << endl; BaseB* pbb = (BaseB *)(pa);//暴力强制类型转换,不会修改指针的值 pbb->func();//期望BaseB::func(),实际BaseA::func() cout << "pbb = " << pbb << endl;//期望与pb的地址相同,实际与pa的地址相同 cout << endl; BaseB* pbbb = dynamic_cast<BaseB*>(pa);//编译器先从对象d的地址得到这个对象,然后从前对象树树上找到BaseB,并且修改指针的值到BaseB pbbb->func();//期望BaseB::func(),实际上也是 cout << "pbbb = " << pbbb << endl;//期望与pb的地址相同,实际也是 return 0; } //输出结果 /* sizeof(Derived) = 8 BaseA::func() BaseB::func() pa = 0xbfb3b368 pb = 0xbfb3b36c BaseA::func() pbb = 0xbfb3b368 BaseB::func() pbbb = 0xbfb3b36c */
二、正确使用多重继承:一些工程的建议
(1)、先继承自一个父类,然后实现多个多个接口
(2)、父类中提供equal()成员函数
(3)、equal()成员函数用于判断指针是否指向当前对象
(4)、与多重继承相关的强制类型转换用dynamic_cast完成
#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); } }; //接口1 class Interface1 { public: virtual void add(int i) = 0; virtual void minus(int i) = 0; }; //接口2 class Interface2 { public: virtual void multiply(int i) = 0; virtual void divide(int i) = 0; }; //看起来虽然很像多继承,但除了Base类外,其它的都是接口 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; // 100; pInt1->add(10); //i == 110; pInt2->divide(11); //i == 110 / 11 pInt1->minus(5); //i = 10 - 5; pInt2->multiply(8); //i = 5 * 8; cout << "p->getI() = " << p->getI() << endl; //40 cout << endl; //注意,通过dynamic_cast将pInt1和pInt2指针转为指向d对象中Base对子对象部分, //这样尽管pInt1和pInt2的值不同,但他们都转为同一个Base指针,然后通过Base类的 //equal函数,判断转换后的两个指针是否相等,从而可以判断他们是不是指向同一个 //对象d。
//这样就解决了多重继承多个地址的问题 cout << "pInt1 == p: " <<p->equal(dynamic_cast<Base*>(pInt1)) << endl; //1 cout << "pInt2 == p: " <<p->equal(dynamic_cast<Base*>(pInt2)) << endl; //1 return 0; } //输出结果 /*
p->getI() = 100 p->getI() = 40 pInt1 == p: 1 pInt2 == p: 1 */
三、小结
(1)、多继承可能出现多个虚函数表指针
(2)、与多继承相关的强制类型转换用dynamic_cast完成
(3)、工程开发中使用单继承多接口的方式使用多继承
(4)、父类提供equal()成员函数用于判断指针是否指向当前对象