我有一个关于C ++析构函数行为的问题,更多的是出于好奇而不是其他任何问题。我有以下课程:
Base.h
class BaseB;
class BaseA
{
public:
virtual int MethodA(BaseB *param1) = 0;
};
class BaseB
{
};
Imp.h
#include "Base.h"
#include <string>
class BImp;
class AImp : public BaseA
{
public:
AImp();
virtual ~AImp();
private:
AImp(const AImp&);
AImp& operator= (const AImp&);
public:
int MethodA(BaseB *param1) { return MethodA(reinterpret_cast<BImp *>(param1)); }
private:
int MethodA(BImp *param1);
};
class BImp : public BaseB
{
public:
BImp(std::string data1, std::string data2) : m_data1(data1), m_data2(data2) { }
~BImp();
std::string m_data1;
std::string m_data2;
private:
BImp();
BImp(const BImp&);
BImp& operator= (const BImp&);
};
现在,问题在于使用此代码,一切都可以正常运行。但是,当我为BImp虚拟构建析构函数时,在调用AImp :: MethodA时,类BImp似乎没有初始化其数据(m_data1和m_data2)。我已经检查并确保所包含的数据在施工时是正确的,所以我想知道这背后的原因是什么......
干杯!
编辑:param1实际上是MethodA中对B的引用。看起来我过度清理了我的真实代码!
编辑2:重新排列代码以显示两个不同的文件。测试了这段代码编译好了。对此感到抱歉!
答案 0 :(得分:9)
如果您在相关类型之间进行转换,就像在这种情况下那样you should use static_cast
or dynamic_cast
,而不是reinterpret_cast
,因为编译器可能在投射时调整对象指针值更多派生类型。在这种情况下,reinterpret_cast
的结果是未定义的,因为它只取指针值并假装它是另一个对象,而不考虑对象布局。
答案 1 :(得分:2)
MethodA按值获取其参数。这意味着传递副本(并且必须销毁副本)。这是我最好的猜测,为什么你可能有一个你没想到的BImpl被摧毁,但我不知道A的析构函数的虚拟或非虚拟性质可能与它有什么关系。
但是这段代码无法编译 - 你在B中声明虚函数时使用了B类,但是直到稍后才定义B.而且我不知道该演员会发生了什么 - 你无法重新解释播放类类型。也许如果你编写一个演示你的问题的测试用例,并发布它?
答案 2 :(得分:1)
这段代码中有很多不确定的东西,所以我很惊讶它在任何情况下都可以工作或编译。
MethodA
B
将BImp
投放到reinterpret_cast
- 糟糕的主意!如果您要向这个方向投掷,dynamic_cast
是最安全的。答案 3 :(得分:1)
几条评论:
您的基类应该具有虚拟析构函数,因此在删除对象时,将调用派生类'dtor而不是基类dtor。
MethodA将BaseB指针作为参数仅将指针重新解释为BImp(BaseB的派生类)是危险的。除了将BImp传递给MethodA之外,无法保证其他内容。如果只有一个BaseB对象是MethodA会发生什么?我怀疑可能会有很多不好的事情。
我猜你的代码“完美无缺”,因为你只将BImp传递给MethodA。如果您只是将BImp传递给MethodA,那么使签名与intent相匹配(这具有删除该可怕的重新解释调用的额外好处)。
答案 4 :(得分:0)
您的代码格式错误。它不是有效的C ++。在C ++语言中,reinterpret_cast
只能用于在指针类型,引用类型之间进行转换,以执行指针到整数的转换(在任一方向上)。
在您的代码中,您尝试使用reinterpret_cast
将B
类型转换为BImp
类型。这在C ++中显然是非法的。如果您的编译器允许使用此代码,则必须查阅编译器的文档以确定正在进行的操作。
其他回复已经提到过“切片”。请记住,这只是对特定编译器的特定非标准行为的猜测。它与C ++语言无关。