static_cast到base析构函数的指向派生类的安全性

时间:2015-03-08 16:02:56

标签: c++ casting language-lawyer static-cast

这是问题Downcasting using the Static_cast in C++Safety of invalid downcast using static_cast (or reinterpret_cast) for inheritance without added members

的变体

我不清楚标准" B中的短语实际上是D类型对象的子对象,结果指针指向D&#34型的封闭对象;关于~B的行为。如果你在~B中转换为D,那么它仍然是一个子对象吗? 以下简单示例显示了问题:

void f(B* b);

class B {
public:
  B() {}
  ~B() { f(this); }
};

class D : public B { public: D() {} };

std::set<D*> ds;

void f(B* b) {
  D* d = static_cast<D*>(b);  // UB or subobject of type D?
  ds.erase(d);
}

我知道演员是一个敞开大门的灾难,从dtor做这样的事情是一个坏主意,但同事声称&#34;代码是有效的,并且正常工作。演员表是完全有效的。评论明确指出不应该取消引用&#34;。

我指出演员阵容是不必要的,我们应该更喜欢类型系统提供的评论。令人遗憾的是,他是高级/首席开发人员之一,也是一位经验丰富的c ++&#34;专家&#34;。

我可以告诉他演员阵容是UB吗?

3 个答案:

答案 0 :(得分:5)

[expr.static.cast] / P11:

  

类型为“指向 cv1 B的指针的prvalue,”其中B是类类型,可以   转换为“指向 cv2 D的指针”的prvalue,其中D是   如果是有效的标准转换,则从B派生类(第10条)   从“指向D”到“指向B的指针”存在(4.10), cv2 是   与 cv1 相同的cv-qualification,或更高的cv资格,和   B既不是D的虚基类,也不是a的基类   D的虚拟基类。空指针值(4.10)被转换   到目标类型的空指针值。如果是prvalue   键入“指向 cv1 B的指针”指向实际为子对象的B   类型为D的对象,结果指针指向   包含D类型的对象。否则,行为未定义。

那么,问题是,在static_cast时,指针实际上是否指向“实际上是B类型对象的子对象的D” 。如果是这样,就没有UB;如果没有,则行为未定义是否取消引用或以其他方式使用结果指针

[class.dtor] / p15说(强调我的)

  

为对象调用析构函数后,对象不再   存在

和[basic.life] / p1说

  

类型T的对象的生命周期在以下时间结束:

     
      
  • 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或
  •   
  • [...]
  •   

然后,D对象的生命周期一旦调用析构函数就结束了,当然B的析构函数开始执行时 - {{1}之后析构函数体已完成执行。此时,没有“D类型的对象”,这个D可以是 - 它“不再存在”的子对象。因此,你有UB。

如果B是多态的(给定虚函数),则支持UBsan将report an error使用此代码。

答案 1 :(得分:2)

显然你的同事的印象是,只要你没有取消引用无效指针,你就可以了。

错误

仅仅评估这样的指针有未定义的行为。这段代码显然已被破坏。

答案 2 :(得分:0)

你应该明确地告诉他,这是UB! !

为什么?

  

12.4 / 7:基础和成员按照构造函数完成的相反顺序销毁。对象在   他们的建设逆序。

     

12.6.2 / 10:第一个(...)虚拟基类被初始化(...)然后,直接基类被初始化

因此,当破坏D时,首先破坏D成员然后破坏D子对象,然后才破坏B。

此代码确保在销毁B对象时调用f()

 ~B() { f(this); } 

因此,当D对象被销毁时,首先销毁D子对象,然后执行~B(),调用f()

f()中,您将指针指向B作为指向D的指针。这是UB:

  

3.8 / 5:(...)在对象的生命周期结束后,在重用或释放对象占用的存储空间之前,   指向对象所在存储位置的指针   或被找到   可能会被使用,但只能以有限的方式使用。   (...)如果指针用于访问非静态数据成员或调用非静态成员函数,则程序具有未定义行为   object,或(...)指针用作static_cast 的操作数。