gpt4 book ai didi

c++ - 虚拟继承使应用程序崩溃

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:14:21 25 4
gpt4 key购买 nike

以下代码崩溃(访问冲突错误),因为我使用了虚拟继承。
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;
}

最佳答案

问题是转换链不正确: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

在这种情况下,&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

在这种情况下,&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

这里的挑战是虚拟基地的单个实例应该在所有潜在基地之间共享。由于只有完整对象知道将涉及哪些基础,一个简单的选择是让完整对象负责放置虚拟基础(它放在尾部)并让虚拟表提供导航机制,在运行时,来自 Object派生类。

但是请注意,在我们设计的情况下&base1 != &object , &base2 != &object&derived != &object因为object放在尾部。

这就是为什么使用 C++ 机器 执行转换很重要,它知道如何静态或动态(取决于情况)计算从一个基到另一个基时所需的指针调整。

注意:C++ 机器知道计算是静态的还是动态的,例如 static_cast<Base1*>(&object)是一个编译时错误,dynamic_cast这里是必需的。

关于c++ - 虚拟继承使应用程序崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23056784/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com