对象切片或 UB 风险?

Object slicing or UB risk?

有几个基本 class 是我无法控制的:-

class BaseNode // Just a POD class. No VTable
{
  void foo();
}

class BaseHost 
{
public:
  BaseNode *getNode()
  {
    ...
  } 
}

我想要"extend"BaseNode::foo的功能,但是class被有效地密封了。

建议如下:-

class MyNode: public BaseNode 
{
  void foo()
  {
    // do my stuff..., then
    BaseNode::foo();
  }    
}

class MyHost: public BaseHost 
{
public:
  MyNode *getNode()
  {
    return (MyNode*) BaseHost::getNode(); // Slicing or UB risk?
  }   
}

如果 MyNode 引入额外的 class 成员或虚拟方法,事情会变得很糟糕 - 但如果满足这些限制,我还有 UB 吗?

任何其他陷阱,或者我是否完全重新考虑设计。?

这种向下转型要格外小心:它很容易导致UB。

标准保证您可以安全地从 MyNode* 转换为 BaseNode*

4.10/3: A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class of D. If B is an inaccessible or ambiguous base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

但让我们玩火吧:标准还允许您在特定条件下从 BaseNode* 投射到 MyNode*

5.2.9/11: A prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists, cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The null pointer value is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the behavior is undefined

我尝试将这些引用翻译成纯文本:

  • 如果您确定 BaseHost::getNode() return 是指向 MyNode 对象的向上转换指针,并且您的 class 层次结构中没有虚拟继承,那么它是行。
  • 但是如果 BaseHost::getNode() 会 return 其他东西(例如指向普通 BaseNodeBaseNode 的另一个同级派生的指针),您将拥有 UB。

如前所述,这很危险:UB 可能已经在指针转换期间发生,甚至在您尝试取消引用指针之前。所以最好尽量避免它。如果你有一个多态的 BaseNode class(例如,使用虚拟析构函数),你可以使用更安全的 dynamic_cast