【问题标题】:Safe to cast pointer to a forward-declared class to its true base class in C++?将指向前向声明类的指针转换为 C++ 中的真正基类是否安全?
【发布时间】:2010-05-05 16:38:26
【问题描述】:

在我的一个头文件中:

#include "BaseClass.h"
// a forward declaration of DerivedClass, which extends class BaseClass.
class DerivedClass ;
class Foo {
   DerivedClass *derived ;
   void someMethod() {
       // this is the cast I'm worried about.
       ((BaseClass*)derived)->baseClassMethod() ;
   } 
};

现在,DerivedClass(在它自己的头文件中)是从 BaseClass 派生的,但是编译器在读取上面的 Foo 类定义时并不知道这一点。但是 Foo 指的是 DerivedClass 指针,而 DerivedClass 指的是 Foo 指针,所以它们不能都知道彼此的声明。

第一个问题是在没有派生类的完整定义的情况下,将派生类指针转换为其基类指针类型是否安全(根据 C++ 规范,而不是在任何给定的编译器中)。

第二个问题是是否有更好的方法。我知道我可以将 someMethod() 的主体移出类定义,但在这种情况下,内联它很重要(实际测量热点的一部分 - 我不是在猜测)。

【问题讨论】:

  • 我建议使用 C++ 的强制转换运算符而不是 C 风格的强制转换。你会遇到更少的麻烦,编译器会给你更多的信息。在这里,编译器正在执行reinterpret_cast,这几乎肯定不是您想要的。
  • 如果您希望函数为inline,请将其标记为inline。无需在类中定义它。您仍然可以在头文件中定义它,以便该定义在包含该头文件的任何 TU 中可用。编译器可能会像您在类定义中定义它一样实际内联它。在这两种情况下都不能保证是内联的。
  • @SteveJessop 也许 OP 想要一些简单的示例代码来说明问题?

标签: c++ pointers casting


【解决方案1】:

它可能有效,但风险很大。

问题是大多数时候Derived*Base* 在底层确实具有相同的值(您可以打印)。但是,一旦您拥有虚拟继承和多继承,情况就不是这样了,而且标准肯定不能保证。

当使用static_cast 时,编译器执行必要的算术(因为它知道类的布局)来调整指针值。但这种调整不是由reinterpret_cast 或C 风格的演员执行的。

现在,您可以像这样完美地修改您的代码:

// foo.h
class Derived;

class Foo
{
public:
  void someMethod();
private:
  Derived* mDerived;
};

// foo.cpp
#include "myProject/foo.h"

#include "myProject/foo.cpp"

void Foo::someMethod() { mDerived->baseClassMethod(); }

无论如何:您使用的是指向 Derived 的指针,但您应该注意一些问题:特别是如果您打算 new 类的一个实例,您需要重新定义类的Copy ConstructorAssignment OperatorDestructor 以正确处理指针。还要确保在每个 Constructor 中初始化指针值(无论是 NULL 还是 Derived 的实例)。

这并非不可能,但要记录自己。

【讨论】:

    【解决方案2】:

    您应该使用 c++ 样式转换,而不是 c 样式。原因是你正在做的实际上是一个 reinterpret_cast,也就是说,没有一些罕见的用途,几乎普遍不安全。

    您正在执行的演员阵容未正确完成。您可能可以安全地在两者之间进行转换,这是实现定义的(标准仅保证 void* 能够保存指向任何类型的指针),但如果您之后实际使用新指针,您将召唤鼻恶魔。

    大多数时候,这些鼻恶魔是良性的,您不会注意到它们(在相当多的实现中)。但是,如果您使用多重继承,这些相同的鼻恶魔将变得非常狡猾。即便如此,您也不应该将未定义的行为视为“安全”或其他任何本质上不好的行为,除非根本没有其他方法可以得到您需要的东西(作为一名专业人士,我从未遇到过)。

    这带来了不使用 c 样式转换的全部原因。有一些非常、非常少和非常罕见的情况,但作为一名专业人士,我从来没有遇到过。 c 风格转换的问题在于,它们会根据情况的语义默默地更改为不同类型的转换。在您不知情的情况下,单独代码区域中的微小代码更改可能会严重改变强制转换操作。当语义更改为需要不同类型的强制转换时,c++ 风格的强制转换会通知您,c 风格的强制转换只会默默地改变;当使用 c 风格的强制转换时,您可以从已定义的行为转到未定义的行为,而没有任何线索表明它已经发生了。

    【讨论】:

      【解决方案3】:

      您在这里拥有的是 C++ 术语中的 reinterpret_cast(因为“已知是安全的”static_cast 会导致编译器错误),我对此非常怀疑。

      难道不能转发声明 Foo 为 Derived 吗?

      【讨论】:

        猜你喜欢
        • 2015-08-12
        • 1970-01-01
        • 2023-03-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多