【编程语言】如何解决菱形继承问题
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)来解决菱形继承问题。这样做避免了菱形继承造成的二义性以及内存浪费问题,但增加了内存布局的复杂性,此外引入新的虚基类表又不可避免使用指针进行数据的存取,这将降低程序的执行效率。
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)来解决菱形继承问题。这样做避免了菱形继承造成的二义性以及内存浪费问题,但增加了内存布局的复杂性,此外引入新的虚基类表又不可避免使用指针进行数据的存取,这将降低程序的执行效率。
相关文章推荐
- 菱形继承问题和虚继承是如何解决二义性与数据冗余的
- 剖析C++是如何解决菱形继承的二义性和数据冗余的
- 菱形继承产生的问题及解决
- 菱形继承问题分析及其在C++的解决方法(虚继承)
- CSS3中的opacity透明度属性的继承问题如何解决
- 1:分析菱形继承的问题。 2:剖析虚继承是怎么解决二义性和数据冗余的。
- 通过菱形继承剖析虚继承解决二义性和数据冗余问题
- c++菱形继承产生的问题及解决
- 如何解决ALV的负数符号前显的问题
- 如何用MediaCapture解决二维码扫描问题
- 如何解决在.Net中用Sql语句向SqlServer数据库中插入特殊字符失败的问题?
- 如何解决 Java 安全问题?
- 在arcgis使用python脚本进行字段计算时是如何解决中文问题的
- 如何解决443端口被System进程占用,导致https访问只能带着端口号的问题
- 如何解决PHP中文乱码问题?
- 如何解决时间超过2038问题
- 如何解决win10语言栏消失变成空白问题
- 如何使用 TRACERT 解决 Windows 中的 TCP/IP 问题
- 4000 如何解决IE6下input边框问题
- c sharp 中调用mo如何解决引用问题?