2

我有一个关于 C++ 析构函数行为的问题,更多的是出于好奇。我有以下课程:

基数.h

class BaseB;

class BaseA
{
    public:
        virtual int MethodA(BaseB *param1) = 0;
};

class BaseB
{
};

小鬼

#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 的引用。看起来我对我的真实代码进行了过多的清理!

Edit2:重新排列代码以显示两个不同的文件。测试了这段代码可以编译,不错。对于那个很抱歉!

4

5 回答 5

9

如果像在这种情况下那样在相关类型之间进行转换,则应该使用static_castordynamic_cast而不是reinterpret_cast,因为编译器可能会在将对象指针值转换为更派生的类型时调整它。在这种情况下,结果reinterpret_cast是未定义的,因为它只是获取指针值并假装它是另一个对象,而不考虑对象布局。

于 2009-12-20T23:06:29.113 回答
2

MethodA 按值获取其参数。这意味着传递了一个副本(并且必须销毁该副本)。这是我最好的猜测,为什么您可能会破坏您没想到的 BImpl,但我看不出 A 的析构函数的虚拟或非虚拟性质可能与它有什么关系。

但是这段代码无法编译——你使用类 B 在 A 中声明虚函数,但 B 直到稍后才定义。而且我不知道该演员表发生了什么-您无法重新解释演员表类型。也许如果您编写一个证明您的问题的测试用例,然后发布?

于 2009-12-20T23:03:07.733 回答
1

这段代码中有很多不确定的东西,所以我很惊讶它在任何情况下都能工作或编译。

  • 通过值而不是引用传递参数MethodA
  • 将 a 投射BBImpvia reinterpret_cast—— 坏主意!如果你要朝那个方向投,dynamic_cast是最安全的。
  • 我看不出您应该如何从 B 中获取 BImp。您没有调用任何构造函数,也没有可以调用的可以接受 B 的构造函数。您的 BImp 的默认构造函数是私有的,并分配没有数据的 B,转换为仍然没有数据的 BImp,转换为 BImp,仍然不会给你任何数据!
于 2009-12-20T23:10:17.743 回答
1

几条评论:

  • 您的基类应该具有虚拟析构函数,以便在删除对象时调用派生类的 dtor 而不是仅调用基类 dtor。

  • MethodA 将 BaseB 指针作为参数仅将指针重新解释为 BImp(BaseB 的派生类)是危险的。不能保证将 BImp 以外的其他内容传递给 MethodA。如果只有一个 BaseB 对象是 MethodA 会发生什么?我怀疑可能有很多坏事。

  • 我猜你的代码“完美无缺”,因为你只将 BImp 传递给 MethodA。如果您只是将 BImp 传递给 MethodA,则使签名与意图相匹配(这具有删除那个可怕的重新解释调用的额外好处)。

于 2009-12-21T17:00:14.163 回答
0

您的代码格式不正确。它不是有效的 C++。在 C++ 语言reinterpret_cast中,只能用于在指针类型、引用类型之间进行强制转换,以执行指针到整数的转换(在任一方向上)。

在您的代码中,您尝试使用reinterpret_cast从 type 转换B为 type BImp。这在 C++ 中是明确非法的。如果您的编译器允许此代码,您必须查阅编译器的文档以确定发生了什么。

其他回复已经提到“切片”。请记住,这只不过是对特定编译器的特定非标准行为的猜测。它与 C++ 语言无关。

于 2009-12-20T23:37:31.580 回答