c++類的某個成員的析構函數是刪除的,會導致類合成的默認構造函數也是刪除的?

c++ Primer 5th中文版第13章451頁中有這樣一段話:

一個成員有刪除的或不可訪問的析構函數會導致合成的默認和拷貝構造函數被定義為刪除的,這看起來可能有些奇怪。其原因是,如果沒有這條規則,我們可能會創建出無法銷毀的對象。

用代碼描述的話大概是這樣:

class A
{
int a;
public:
~A()=delete;
};
class B
{
A obj_A;
//public:
//B(){}
};
int main()
{
B* pb=new B;
return 0;
}

這樣運行的結果如下(ubuntu14.04+clang3.6):

對於書上的解釋我有一些疑問:

1、如果說是為了避免產生無法銷毀的對象,那麼A的合成的默認構造函數也應該是刪除的。而實際上在main函數中寫上 A* pa=new A; 這樣一條語句,只要不delete pa,編譯器就不會報錯。當然這個結果我是理解的,沒有什麼問題。

2、由於上面的報錯信息和primer上面的描述幾乎相同,仍不好理解。如果我把B的默認構造函數顯示定義出來(取消上面代碼中的注釋),報錯信息就變成這樣

報錯信息中顯示B::B()嘗試使用一個已刪除的函數A::~A(),也就是說這才是B::B()被隱式刪除的根本原因。但是B::B()中應該只會使用A::A(),怎麼會使用A::~A()呢?

目前我能想到的一種可能就是跟異常處理相關。如果我將代碼改成這樣:

class A
{
int a;
public:
A(){}
~A(){}
};
class B
{
A obj_A;
public:
B() { throw "exception"; }
};
int main()
{
try{
B* pb=new B;
}catch(const char *s)
{}
return 0;
}

用gdb可以觀察到B::B()的反彙編是這樣的:

可以看到在B::B()中的確調用了A::~A()。然而如果我將異常相關的代碼去掉,則B::B()的反彙編是這樣的:

可以看到B::B()中並沒有出現調用A::~A()的指令。在此情況下我覺得即便是顯示刪除A的析構函數也不會有什麼問題。然而事實剛好相反,編譯器還是報錯了。所以現在我有點懵了,到底是為什麼B::B()中會使用A::~A()呢?

跪求大神解答


萬一B::B()拋了異常呢?你看到的A::~A()的調用應該就是catch裡面的代碼。至於為什麼沒有調用A::A(),可能是inline後被刪除了,A就是一個POD。


我補充一點,C++標準 &> 12.1-Constructor中(4.7)小節描述如下:

A defaulted default constructor for class X is defined as deleted if:

(4.7) — any potentially constructed subobject has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.

C++標準給出了具體的描述,但是沒有給出原因。

題主點明構造函數在異常處理場景下需要調用數據成員A的析構函數A::~A(),例如構造函數中有new operator調用等情況確實有可能出現異常。但是如果使用C++11中的noexcept修飾B::B(),明確告知編譯器保證B::B()不會拋出異常,此時B::B()的確不會調用A::~A(),見https://godbolt.org/g/BrcOhP。雖然noexcept修飾的B::B()不會調用A::~A(),但是"~A() = delete;"編譯還是不會通過,見https://godbolt.org/g/tdg2p0。

那是否表明編譯器對B::B()進行語義分析的時候,是無腦按照C++標準12.1-Constructor (4.7)執行的,無論B::B()是否存在調用A::~A()的需要?


推薦閱讀:

編譯器簡介: 在 Siri 前時代如何與計算機對話
來自YSRC:孤挺花字元串混淆功能分析
編譯器入門

TAG:CC | 編譯器 | CPrimer | C11 |