您的位置:首页 > 编程语言

【编程语言】如何解决菱形继承问题

2012-05-11 22:28 253 查看
继承、封装和多态是面向程序设计(OOP)的三大特点,而它们三者之中最具实际操作性的当属继承。通过继承可以实现简单功能的组合和定制,而多重继承更将这种能力发挥到更高的境界。不过事事都有弊端,如果使用多重继承不当很容易造成菱形继承问题(diamond problem)。Bjarne Stroustrup用下面这个例子描述菱形继承问题:

class storable //this is the our base class inherited by transmitter and receiver classes

{

public:

virtual void read();

virtual void write();

private:

....

}

class transmitter: public storable

{

public:

void write();

...

}

class receiver: public storable

{

public:

void read();

...

}

class radio: public transmitter, public receiver

{

public:

void read();

....

}

transmitter和receiver都从storable派生而来,而radio则从transmitter和receiver派生而来,是最终派生类。这样的继承关系很特殊,因为从storable到radio存在两条不同的路径,即radio将从两条不同的继承路径获得storable的成员。如果不采取措施应对菱形继承问题的话,编译程序时将收到许多二义性的错误提示。如在上述示例中,如果radio类的对象调用write()方法,编译器将无法确定应当调用transmitter中的write()方法或是receiver中的write()方法。解决的方法很简单:只需在transmiiter和receiver类的继承列表前添加virtual关键字即可,如下列代码所示:

class storable

{

public:

int istorable;

virtual void read()

{

cout<<"read() in storable called.\n";

}

virtual void write()

{

cout<<"write() int storable called.\n";

}

};

// class transmitter

class transmitter : virtual public storable

{

public:

int itransmitter;

void read()

{

cout<<"read() in transmitter called.\n";

}

};

// class receiver

class receiver : virtual public storable

{

public:

int ireiceiver;

void write()

{

cout<<"write() in receiver called.\n";

}

};

// class radio

class radio : public transmitter, public receiver

{

public:

int iradio;

};

为了方便描述使用虚拟继承后radio类对象的内存布局,我在各类中都增加了一个整型的成员变量。

现在编写主函数来打印radio类对象的内存布局,代码如下所示:

typedef void (*FUN)();

// main function

int main()

{

radio r;

r.istorable = 1;

r.itransmitter = 2;

r.ireiceiver = 3;

r.iradio = 4;

cout<<"address of r:"<<(INT_PTR*)&r<<endl;

cout<<"vbtab of transmitter:\n";

cout<<" [1] "<<((INT_PTR*)(*(INT_PTR*)&r))[0]<<endl;

cout<<" [2] "<<((INT_PTR*)(*(INT_PTR*)&r))[1]<<endl;

cout<<"itransmitter = "<<*((INT_PTR*)&r + 1)<<endl;

cout<<"vbtab of receiver:\n";

cout<<" [1] "<<((INT_PTR*)*((INT_PTR*)&r + 2))[0]<<endl;

cout<<" [2] "<<((INT_PTR*)*((INT_PTR*)&r + 2))[1]<<endl;

cout<<"ireiceiver = "<<*((INT_PTR*)&r + 3)<<endl;

cout<<"iradio = "<<*((INT_PTR*)&r + 4)<<endl;

cout<<"address of storable part:"<<(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1])<<endl;

cout<<"_vptr of storable:"<<(INT_PTR*)*(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1])<<endl;

cout<<" [1] "<<(INT_PTR*)*(INT_PTR*)*(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1])<<" ";

((FUN)(*(INT_PTR*)*(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1])))();

cout<<" [2] "<<(INT_PTR*)*((INT_PTR*)*(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1]) + 1)<<" ";

((FUN)*((INT_PTR*)*(INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1]) + 1))();

cout<<"istorable = "<<*((INT_PTR*)((char*)&r + ((INT_PTR*)(*(INT_PTR*)&r))[1]) + 1)<<endl;

system("pause");

return 0;

}

运行结果如下所示:

address of r:0021F73C

vbtab of transmitter:

[1] 0

[2] 20

itransmitter = 2

vbtab of receiver:

[1] 0

[2] 12

ireiceiver = 3

iradio = 4

address of storable part:0021F750

_vptr of storable:002F790C

[1] 002F12A8 read() in transmitter called.

[2] 002F100F write() in receiver called.

istorable = 1

请按任意键继续. . .

从上面的结果不难看出,VC++编译器引入虚基类表(virtual base table)来解决菱形继承问题。这样做避免了菱形继承造成的二义性以及内存浪费问题,但增加了内存布局的复杂性,此外引入新的虚基类表又不可避免使用指针进行数据的存取,这将降低程序的执行效率。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: