从基析构函数static_cast指向派生类的指针的安全性
Safety of static_cast to pointer-to-derived class from base destructor
这是问题"使用C++中的Static_cast向下转换"和"使用static_cast(或reinterpret_cast)进行继承而不添加成员的无效向下转换的安全性"的变体
我不清楚标准中的短语"B 实际上是 D 类型的对象的子对象,生成的指针指向 D 类型的封闭对象"关于 ~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 做这样的事情都是一个坏主意,但一位同事声称"代码有效且工作正常。这个演员表是完全有效的。该评论明确指出,不应取消提及"。
我指出,强制转换是不必要的,我们应该更喜欢类型系统提供的保护而不是注释。可悲的是,他是高级/首席开发人员之一,也是所谓的 c++ "专家"。
我可以告诉他演员是UB?
[expr.static.cast]/p11:
类型为"指向 cv1 的指针
B
"的 prvalue,其中 B 是类类型,可以 转换为类型为"指向 CV2D
的指针"的 prvalue,其中D
是 类派生自B
(条款 10),如果有效的标准转换 从"指向D
的指针"到"指向B
的指针"存在(4.10),cv2是 与CV1相同或更高的CV资格,以及B
既不是D
的虚拟基类,也不是D
的虚拟基类 .空指针值 (4.10) 被转换 到目标类型的空指针值。如果 prvalue 的 类型"指向 CV1B
的指针"指向实际上是子对象的B
对于类型为D
的对象,生成的指针指向 封闭类型D
的对象。否则,行为是未定义的。
那么,问题是,在static_cast
时,指针是否实际上指向"实际上是类型D
对象的子对象的子对象的B
"。如果是这样,则没有 UB;如果不是,则无论是否取消引用或以其他方式使用生成的指针,行为都是未定义的。
[class.dtor]/p15 说(强调我的)
为对象调用析构函数后,该对象将不再 存在
和 [basic.life]/p1 说
类型
T
对象的生存期在以下情况下结束:
- 如果 T 是具有非平凡析构函数 (12.4) 的类类型,则析构函数调用将启动,或者
- [...]
因此,一旦调用D
的析构函数,对象的生存期就结束了,当然,当B
的析构函数开始执行时 - 也就是在D
的析构函数主体完成执行之后。此时,没有"类型D
的对象"可以成为该B
的子对象 - 它"不再存在"。因此,你有 UB。
如果 UBsan 的 Clang 是多态的(给定一个虚函数),则会报告此代码的错误B
该代码支持此读取。
显然,你的同事的印象是,只要你不取消引用无效的指针,你就没问题。
他错了。
仅评估此类指针具有未定义的行为。这段代码显然被破坏了。
你应该明确地告诉他这是 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:(...) 在对象的生存期结束后,在对象占用的存储被重用或释放之前,任何 指向对象将所在的存储位置的指针 或位于 可以使用,但只能以有限的方式使用。 (...)如果指针用于访问非静态数据成员或调用 对象,或 (...指针用作static_cast的操作数。
- 为什么使用 "this" 指针调用派生成员函数?
- 如何使用基类指针引用派生类成员
- 将派生指针分配给C 中的其他派生类型
- 如何通过派生指针调用基类中的模板成员函数
- 如何将带有恒定指针作为参数的模板的派生指针使用
- 无法返回包含派生指针的向量
- 如何创建派生指针类变量
- C++强制转换为派生指针或设计错误
- 无法从派生指针访问公共基成员
- C++发送派生指针作为参数
- 将专用基指针强制转换为专用于其他模板参数的派生指针("adding on"专用化)
- 在派生指针上显式调用基类析构函数时编译错误
- 安全派生指针值的示例
- 安全派生指针的整数表示形式
- c++多态克隆:如何从基指针获得派生指针
- 如果我将派生指针转换为基指针,这两个指针是否保证具有相同的值?
- 以任何方式将基指针强制转换为派生指针是否危险
- 将派生指针隐式强制转换为其相应基类的引用
- 当派生指针仅添加方法时,将基指针强制转换为派生指针的有效性
- c++转换指针到派生指针到基指针的例子