2016-12-25 75 views
1

假設我們有一個簡單的結構:派生類的破壞後基類的使用成員

struct RefCounters { 
    size_t strong_cnt; 
    size_t weak_cnt; 
    RefCounters() : strong_cnt(0), weak_cnt(0) {} 
}; 

從實現的角度,析構函數RefCounters::~RefCounters應該什麼都不做,因爲它的所有成員都有基本類型。這意味着如果這個類型的對象被析構函數的顯式調用破壞了(但是它的內存是而不是解除分配),那麼在對象死後,我們將能夠正常地使用它的成員。

現在假設我們有一些從RefCounters派生的類。假設RefCounters僅在Derived類的基類中存在一次。假設爲類Derived的對象顯式調用析構函數,但其​​內存爲而不是解除分配。之後可以訪問會員strong_cntweak_cnt嗎?

從實現的角度來看,應該沒問題,至少在沒有涉及虛擬繼承的情況下。因爲Derived*可以靜態轉換爲RefCounters*(將編譯時常量偏移量加到地址中),並且RefCounters的存儲器不應該被Derived類的析構函數觸及。

下面是一個代碼示例:

struct RefCounted : public RefCounters { 
    virtual ~RefCounted() {} 
}; 

struct Base : public RefCounted { 
    int val1; 
    virtual void print(); 
}; 

struct Derived : public Base { 
    std::string val2; 
    virtual void print(); 
}; 

Derived *pDer = new Derived(); 
pDer->~Derived();   //destroy object 
pDer->strong_cnt++;  //modify its member 
std::cout << pDer->strong_cnt << pDer->weak_cnt << "\n"; 

被認爲是不確定的行爲,例如由代碼C++標準?有沒有實際的原因可能導致它失效?可以通過微小的更改或添加一些約束來使其合法化嗎?

P.S.假設這樣的代碼示例允許創建intrusive_ptr + weak_ptr組合,這樣如果至少有一個weak_ptr指向它,總是可以從對象指針獲取weak_ptr。更多詳情,請參閱this question

+0

在一個不相關的說明中,爲什麼'RefCounters'本身不處理它的計數器呢?意思是析構函數*會做些什麼(即減少一個或兩個計數器)? –

+1

由於'RefCounters'具有一個微不足道的析構函數,因此每當** [basic.life]/1 **時其存儲被重用或釋放時,它的生存期結束。顯式的析構函數調用是不可操作的,不應該影響任何東西; 「RefCounters」的特定實例也不是大對象的子對象。 –

+0

@IgorTandetnik:謝謝你的評論。我想它應該使合法的第一種情況(即沒有派生類)。但我仍然不確定派生類的第二種情況。 – stgatilov

回答

0

我相信你的方法不好。評論中有一個很好的鏈接,顯示了關於標準細節的爭論。一旦有爭論,不同的編譯器就會有不同的實現細節的機會。更。同一編譯器可能會將其實現從一個版本更改爲另一個版本。

你使用各種黑暗角落的次數越多,遇到問題的機會就越大。

底線。什麼願意實現?爲什麼你不能使用普通的C++語言功能來做到這一點?

+0

只需閱讀「P.S.」問題的一部分,我在那裏添加了一個鏈接。但請不要發佈「你不應該這樣做」的東西,因爲這不是一個好的答案。 – stgatilov

+0

@stgatilov,看起來你需要重新修改你的問題,因爲問題「是基類(和它的**原始**數據成員)在銷燬派生類後仍然存在」與討論設計智能指針的方式非常無關。 –

+0

你問「這段代碼是否合法?」,「它可以被修改爲合法嗎?」我的意思是,既然即使在委員會中也存在這樣的爭論,所以應該把疑慮轉移到「非法」的方向。代碼是非法的,至少在辯論得到解決之前,編譯器將與此決議保持一致。 –

相關問題