简体   繁体   English

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

[英]Safety of static_cast to pointer-to-derived class from base destructor

This is a variant of the questions Downcasting using the Static_cast in C++ and Safety of invalid downcast using static_cast (or reinterpret_cast) for inheritance without added members 这是使用C ++中的Static_cast进行Downcasting的问题的变体,以及使用static_cast(或reinterpret_cast)进行无效向下转换的安全性,无需添加成员

I am not clear on the phrase in the standard "B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D" with respect to behavior in ~B. 我不清楚标准“B实际上是D类对象的子对象的短语,结果指针指向类型D的封闭对象”关于~B中的行为。 If you cast to D in ~B, is it still a subobject at that point ? 如果你在~B中转换为D,那么它仍然是一个子对象吗? The following simple example shows the question: 以下简单示例显示了以下问题:

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);
}

I know that the cast is an open door to disaster, and doing anything like this from the dtor is a bad idea, but a co-worker claims "The code is valid and works correctly. That cast is perfectly valid. The comment clearly states that it should not be dereferenced". 我知道演员是一个敞开大门的灾难,从dtor做这样的事情是一个坏主意,但同事声称“代码是有效的,并且工作正常。这个演员是完全有效的。评论明确指出它不应该被解除引用“。

I pointed out that the cast is unnecessary and we should prefer the protection provided by the type system to comments. 我指出演员阵容是不必要的,我们应该更喜欢类型系统提供的评论。 The sad part is that he is one of the senior/lead developers and a supposed c++ "expert". 令人遗憾的是,他是高级/首席开发人员之一,也是一位理想的c ++“专家”。

Can I tell him the cast is UB ? 我可以告诉他演员阵容是UB吗?

[expr.static.cast]/p11: [expr.static.cast] / P11:

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 (Clause 10) from B , if a valid standard conversion from “pointer to D ” to “pointer to B ” exists (4.10), 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 . 类型为“指向cv1 B指针”的prvalue,其中B是类类型,可以转换为类型为“指向cv2 D指针”的prvalue,其中D是从B派生的类(第10条),如果是有效的标准从“指向D指针”到“指向B指针”的转换存在(4.10), cv2cv1相同,或者cv资格比cv1更高 ,并且B既不是D的虚基类也不是基类D的虚拟基类。 The null pointer value (4.10) is converted to the null pointer value of the destination type. 空指针值(4.10)将转换为目标类型的空指针值。 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 . 如果类型的prvalue“指针CV1 B ”指向一个B实际上是类型的对象的子对象D ,将所得指针指向类型的包围对象D Otherwise, the behavior is undefined. 否则,行为未定义。

The question, then, is whether, at the time of the static_cast , the pointer actually points to "a B that is actually a subobject of an object of type D ". 的问题,然后,是,是否在时间static_cast ,指针实际指向“一B实际上是类型的对象的子对象D ”。 If so, there is no UB; 如果是这样,就没有UB; if not, then the behavior is undefined whether or not the resulting pointer is dereferenced or otherwise used . 如果不是,则无论是否取消引用或以其他方式使用结果指针 ,行为都是未定义的。

[class.dtor]/p15 says that (emphasis mine) [class.dtor] / p15说(强调我的)

Once a destructor is invoked for an object, the object no longer exists 一旦为对象调用析构函数,该对象就不再存在

and [basic.life]/p1 says that 和[basic.life] / p1说的那样

The lifetime of an object of type T ends when: 类型T的对象的生命周期在以下情况下结束:

  • if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者
  • [...] [...]

From this, then, the D object's lifetime has ended as soon as its destructor is invoked, and certainly by the time B 's destructor began to execute - which is after D 's destructor body has finished execution. 从那时起, D对象的生命周期一旦被调用析构函数就结束了,当然也就是B的析构函数开始执行时 - 这是在D的析构函数体完成执行之后。 At this point, there is no "object of type D " left that this B can be a subobject of - it "no longer exists". 此时,没有“类型D对象”,这个B可以是 - 它“不再存在”的子对象。 Thus, you have UB. 因此,你有UB。

Clang with UBsan will report an error on this code if B is made polymorphic (given a virtual function), which supports this reading. 如果B是多态的(给定一个虚函数),则支持UBsan将报告此代码的错误 ,这支持此读数。

Apparently your co-worker is under the impression that as long as you do not dereference an invalid pointer, you are fine. 显然你的同事的印象是,只要你没有取消引用无效指针,你就没事了。

He is wrong . 错了

Merely evaluating such a pointer has undefined behaviour. 仅仅评估这样的指针具有未定义的行为。 This code is obviously broken. 这段代码显然已被破坏。

You should definitively tell him that it's UB ! 你应该明确告诉他这是UB! !

Why ? 为什么?

12.4/7: Bases and members are destroyed in the reverse order of the completion of their constructor The objects are detroyed in the reverse order of their constuction. 12.4 / 7:基础和成员按照构造函数完成的相反顺序销毁。对象按其构造的相反顺序销毁。

12.6.2/10: First (...) virtual base classes are initialized (...) then, direct base classes are initialized 12.6.2 / 10:初始化第一个(...)虚拟基类(...)然后,初始化直接基类

So when destructing a D, first the D members are destructed and then the D sub object, and only then will B be destructed. 因此,当破坏D时,首先破坏D成员然后破坏D子对象,然后才破坏B。

This code makes sure that f() is called when the B object is destroyed: 此代码确保在销毁B对象时调用f()

 ~B() { f(this); } 

So when a D object is destroyed, the D suboject is destroyed first, and then ~B() is executed, calling f() . 因此,当D对象被销毁时,首先销毁D子对象,然后执行~B(),调用f()

In f() you cast the pointer to a B as pointer to a D. This is UB: f()您将指针转换为B作为指向D的指针。 这是UB:

3.8/5: (...) after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways . 3.8 / 5: (...)在对象的生命周期结束之后,在重用或释放对象占用的存储之前,可以使用任何指向对象所在或所在的存储位置的指针, 但只是在有限的方式 (...) The program has undefined behavior if the pointer is used to access a non-static data member or call a non-static member function of the object, or (...) the pointer is used as the operand of a static_cast . (...)如果指针用于访问非静态数据成员或调用对象的非静态成员函数,或者(...) 指针用作操作数,则程序具有未定义的行为 。 static_cast

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 什么时候是`static_cast <Base*> (的static_cast <void*> (派生))`从指向派生类的指针有效吗? - When is a `static_cast<Base*>(static_cast<void*>(derived))` from a pointer to a derived class valid? 为什么从指向 Base 的指针到指向 Derived 的指针的 static_cast “无效”? - Why is a static_cast from a Pointer to Base to a Pointer to Derived “invalid?” 从基类指针到派生类指针的static_cast无效 - static_cast from base class pointer to derived class pointer is invalid 将派生对象中的“this”指针的static_cast用于基类的问题 - Question of using static_cast on “this” pointer in a derived object to base class C ++如何在不使用static_cast或dynamic_cast的情况下从基类指针访问派生类成员? - C++ How to access derived class member from base class pointer without using a static_cast or dynamic_cast? static_cast从Derived *到void *到Base * - static_cast from Derived* to void* to Base* 如果派生类包含其他方法和成员,那么static_cast可以从基类到派生类完成吗? - Can static_cast be done from base class to derived class if derived class contains additional methods and members? 通过指向base,static_cast,crtp,删除模板的指针派生的成员 - Member of derived by pointer to base, static_cast, crtp, removing templates "static_cast 将此对象派生到 C++ 中的基类" - static_cast derived this object to base class in C++ 当static_cast从派生类到基类时会发生什么? - What happens when static_cast from derived class to base class?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM