我有一个从非虚拟类派生的虚拟类。但是,当我将c派生类转换为基类时,该类已损坏。我正在使用调试器查看成员变量,并且当我执行该转换时,成员变量都已损坏。当我使用调试器进行转换(可能是虚拟指针)时,我看到有4字节的差异。 对于Ex:
class A//non-virtual class
{
~A();
int fd;
};
class B:public A
{
virtual ~B();
};
现在说B类型的obj的地址是:0x9354ed0。 现在当我转换它(A *)(0x9354ed0)时,调试器将字节移动4个字节。因此,铸造的obj的起始地址是0x935ed4
将派生虚拟类转换为基于非虚拟类的错误是什么? 4字节差异的原因是什么? 什么是正确的投射方式?感谢您的任何意见或解释。
答案 0 :(得分:8)
以下是您所描述的内容。
class A
{
public:
~A();
// other stuff
};
class B: public A
{
public:
virtual ~B();
// other stuff
};
从非虚基类派生类,并使派生类成为虚拟,这是完全合法的。 4个字节的偏移量(可以是8)对应于指针类型的隐藏成员变量,该变量指向类的虚函数表。
答案 1 :(得分:2)
在层次结构中转换指针时,实际的数字指针值可能会发生变化。它发生变化的事实并没有错。这样的演员阵容没有错。数字指针值的变化取决于类的物理布局。这是一个实现细节。
在您的示例中,指针值的4字节更改可能很容易由派生类中存在虚拟表指针引起。
指针值的更改不会破坏任何成员变量。原始帖子中的示例没有显示任何“损坏的成员变量”,一般而言,您对某些成员变量“损坏”的说法没有多大意义。当你提出这样的说法时,请举例说明,以便人们能够理解你在谈论的是什么。
<强> 更新 强>
基类子对象在派生类中可能具有非零偏移的事实立即意味着为了从派生指针类型到基本指针类型执行适当的转换,编译器必须知道源类型和目标类型以及他们之间的关系。例如,如果你这样做
B* pb = /* whatever */;
A* pa = pb;
编译器将知道如何从指针pa
正确地偏移指针pb
。但是你这样做
A* pa = (void *) pb;
编译器会忘记A
和B
之间的关系,无法执行正确的偏移并产生pa
的无效值。效果与
A* pa = reinterpret_cast<A*>(pb);
如果你这样做,结果将毫无意义。所以,就是不要这样做。
当然,如果您手动检查pb
的数值,并发现它是0x12345678
,然后执行
A* pa = (A*) 0x12345678;
你也会得到完全没有意义的结果,因为计算机无法知道它必须执行指针的正确偏移。
这会产生A
成员被“损坏”的幻觉,而实际上唯一被破坏的是你的指针。你就是那个腐化它的人。
答案 2 :(得分:0)
如果那是您正在使用的代码的shell,那么您不会从A类派生出B类。如果您想这样做,则需要执行此操作:
class A
{
public:
virtual ~A();
// other stuff
};
class B: public A
{
public:
virtual ~B();
// other stuff
};
我真的不认为你可以按照你希望的方式来表达你的问题。如果您正在使用虚函数和派生类,则需要在首次使用时将它们声明为虚拟。另外,我不是百分百肯定,但如果你有任何虚拟析构函数,你需要在你的层次结构中拥有所有的虚拟析构函数。
希望有所帮助, NMR
答案 3 :(得分:0)
使用基类指针指向派生类对象是合法的 这被称为运行时多态,即基于动态类型的对象函数被调度。
使用虚函数定义类时,会形成一个包含虚函数地址的表,称为虚函数表。 当您使用虚函数声明类的对象时,会将指向相应虚函数表的虚拟指针插入其中。
答案 4 :(得分:0)
缺少的四个字节确实是对象的vptr;由于A类没有vtable,因此指向A的指针没有vptr。
虽然实际上从另一个没有的类中使用虚拟方法派生类是合法的,但这不是一个好主意。从没有虚方法的类继承通常是不合理的,因为该类不打算以多态方式使用,这是继承的主要原因。如果你这样做,会发生各种不好的事情。如果您尝试通过基类指针使用派生类对象,您可能会遇到可怕的未定义行为(很可能是崩溃)。例如,尝试删除指向派生类对象的基类指针。