ato*_*erz 4 c++ crash virtual-inheritance visual-studio-2008 visual-studio
以下代码崩溃(访问冲突错误),因为我使用了虚拟继承。
AFAIK 虚拟继承通过强制使用类的单个实例来解决 Diamond 问题。在这种情况下,该类Derived仅继承了一个实例,IObject因此应该没有问题,但它会崩溃。
class IObject
{
public:
virtual int getType()=0;
};
class Base : public IObject
{
protected:
int val;
public:
Base() { val = 1; }
virtual int getType();
};
int Base::getType() { return val; }
class Derived : public virtual Base //If I remove the virtual keyword here problem is solved.
{
public:
Derived() { val = 2; }
};
int getVal( void* ptr )
{
return ((IObject*)ptr)->getType();
}
int main()
{
void* ptr = new Derived();
cout << getVal(ptr) << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
问题在于强制转换链不正确:Derived* -> void* -> IObject*是由于混合 C 和 C++ 概念而导致的未定义行为。更具体地说,周围的规则void*是从 C 继承的,没有对对象和层次结构进行任何调整。
因此,解决方案是确保任何循环void*都是一个T -> void* -> T循环:始终通过相同的类型。因此,根据您的情况,您需要Derived* -> IObject* -> void* -> IObject*.
要理解为什么virtual继承会导致问题,您必须了解它如何具体表示的细节(这是实现定义的)。让我们看一下可能的内存表示的示例(大致基于 Itanium ABI)。
线性非虚拟层次结构的实现就像通过组合一样:
struct Base { int a; };
struct Derived: Base { int b; };
struct SuperDerived: Derived { int c; };
+---+---+
| a | b |
+---+---+
^~~~~~~~~ Derived
^~~~~ Derived specific
^~~~~ Base
+---+---+---+
| a | b | c |
+---+---+---+
^~~~~~~~~~~~~ SuperDerived
^~~~~ SuperDerived specific
^~~~~~~~~ Derived
^~~~~ Base
Run Code Online (Sandbox Code Playgroud)
在这种情况下,&derived == &base并且&superderived == &derived一般来说(注意:如果一个层没有虚拟表而下一层有,那么这就会从屋顶上掉下来)。
具有多个基础的层次结构
struct Base1 { int a; };
struct Base2 { int b; };
struct Derived: Base1, Base2 { int c; };
+---+---+---+
| a | b | c |
+---+---+---+
^~~~~~~~~~~~~ Derived
^~~~~ Derived specific
^~~~~ Base2
^~~~~ Base1
Run Code Online (Sandbox Code Playgroud)
在这种情况下,&derived == &base1但是&derived != &base2,我们已经注意到基类不一定与其派生类具有相同的地址。
最后,让我们添加虚拟继承:
struct Object { int a; };
struct Base1: virtual Object { int b; };
struct Base2: virtual Object { int c; };
struct Derived: Base1, Base2 { int d; };
+---+---+
| b | a |
+---+---+
^~~~~~~~~ Complete Base1
^~~~~ Base1 specific
^~~~~ Object
+---+---+
| c | a |
+---+---+
^~~~~~~~~ Complete Base2
^~~~~ Base2 specific
^~~~~ Object
+---+---+---+---+
| b | c | d | a |
+---+---+---+---+
^~~~~~~~~~~~~~~~~ Complete Derived
^~~~~ Derived specific
^~~~~ Incomplete Base1
^~~~~ Incomplete Base2
^~~~~ Object
Run Code Online (Sandbox Code Playgroud)
这里的挑战是虚拟基地的单个实例应该在所有潜在基地之间共享。由于只有完整的对象知道将涉及哪些碱基,因此一个简单的选择是让完整的对象负责虚拟碱基的放置(它放置在尾部),并让虚拟表提供导航机制,在运行时,从Object派生类。
但是,请注意,在我们的设计中&base1 != &object,&base2 != &object和&derived != &object因为object被放置在尾部。
这就是为什么使用C++ 机器执行转换很重要, C++ 机器知道如何静态或动态(取决于情况)计算从一个基数到另一个基数时所需的指针调整。
注意:C++ 机器知道计算是静态的还是动态的,例如static_cast<Base1*>(&object)编译时错误,adynamic_cast在这里是必要的。
| 归档时间: |
|
| 查看次数: |
1231 次 |
| 最近记录: |