簡體   English   中英

static_cast到base析構函數的指向派生類的安全性

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

這是使用C ++中的Static_cast進行Downcasting的問題的變體,以及使用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是類類型,可以轉換為類型為“指向cv2 D指針”的prvalue,其中D是從B派生的類(第10條),如果是有效的標准從“指向D指針”到“指向B指針”的轉換存在(4.10), cv2cv1相同,或者cv資格比cv1更高 ,並且B既不是D的虛基類也不是基類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的析構函數開始執行時 - 這是在D的析構函數體完成執行之后。 此時,沒有“類型D對象”,這個B可以是 - 它“不再存在”的子對象。 因此,你有UB。

如果B是多態的(給定一個虛函數),則支持UBsan將報告此代碼的錯誤 ,這支持此讀數。

顯然你的同事的印象是,只要你沒有取消引用無效指針,你就沒事了。

錯了

僅僅評估這樣的指針具有未定義的行為。 這段代碼顯然已被破壞。

你應該明確告訴他這是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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM