2017-10-06 78 views
2

我有一個班級,他的constructor將一個對象的地址作爲參數。檢查內存是否在堆上?

MyClass(OtherClass * otherClass); 

在這個類的Destructor我嘗試deleteOtherClass實例。

~MyClass() { 
    if(otherClass != nullptr) { 
     delete otherClass; 
    } 
} 

時遇到的問題是,當我把這種constructor我稱之爲從stack而不是從heap的元素,從而如下面我叫它:

MyClass的MyClass的(& otherObject );

因此,當myClass對象超出範圍時,我得到一個異常。如果我的OtherObject變量是在stackheap上聲明的,我該如何推測?換句話說,我怎麼知道我是否可以delete這個對象?

+3

如果你沒有任何物體所有權規則,代碼將出現混亂,你將有這樣的問題。通常傳遞一個指針是「給所有權」該對象。如果你不想這樣做,你需要將其改爲參考。 – tadman

+2

你應該把它作爲你的類合同的一個明確部分,不管它是否「獲得了所有權」的提供的參數,然後讓代碼創建負責指定它的對象 –

+1

考慮使用'shared_ptr ',儘管這意味着你永遠不會有一個「堆棧對象」給它。在安全性方面,這是一件好事 –

回答

5

儘管有系統特定的方法可能能夠判斷內存是堆棧還是堆棧,但實際上並沒有什麼幫助:您可能有一個指向堆中另一個對象成員的指針。內存將在堆上,但您仍然不負責刪除該對象。換句話說:做不是走上你的路!

解決該問題的正確方法是使界面中的所有權語義清晰明瞭,並與此相符。基本上有兩個方向可以走:

  1. 你的類可以故意接管傳入構造指針責任!相反,班級用戶有責任處理這些對象以確保它們在存在對象時有效。如果您可以獲取堆棧(或成員)對象的指針,則用戶必須保證這些對象的有效性,對於其他方式,對於用戶來處理它們可能是完全微不足道的,例如通過管理它們std::unique_ptr<OtherClass>
  2. 您的課程承擔責任全部對象通過,它將delete所有這些。它成爲調用者的責任,而不是而不是傳遞指向其他地方管理的對象的指針,例如指向堆棧或成員對象上的對象。

有一種混合的方法,你的班級有時對對象負責,但並非總是如此。但是,這種方法的實現實際上是上述兩種方法的組合:您將採用合適的智能指針作爲構造函數參數,並且用戶有責任確保智能指針由您的用戶類。例如,你的班級可能需要一個std::shared_ptr<OtherClass>,其中正常的施工將是delete的對象。當用戶想要傳遞指向另外擁有的對象的指針時,std::shared_ptr<OtherClass>將被構造成具有刪除器,其不是而是delete指針。下面是一個簡單的程序展示了兩種不同的管理策略std::shared_ptr

#include <iostream> 
#include <memory> 

struct foo { 
    char const* name; 
    foo(char const* name) 
     : name(name) { 
     std::cout << "foo::foo(" << name << "): " << this << "\n"; 
    } 
    ~foo() { 
     std::cout << "foo::~foo(" << name << "): " << this << "\n"; 
    } 
}; 

int main() { 
    std::shared_ptr<foo>(new foo("heap")); 
    foo f("stack"); 
    std::shared_ptr<foo>(&f, [](auto){}); 
} 
+0

通過系統特定的方法,你可能指的是找到值堆棧指針(esp或rsp)和堆棧底部(esp + offset)。然後你比較這個值並檢查它是否在堆棧範圍內。但請記住,如果您這樣做,您的代碼將無法移植。 – Asesh

+0

@Asesh:是的,確實如此:雖然我知道在大多數系統中[當前(*)]可以指示指針是指堆還是棧,但我明顯推薦*不*使用這種方法。 (*)如果/當[stackful]協程變得可用時,將很難判斷指針是指向堆還是棧,因爲協程棧會來自堆。然而,這只是*不利用對象位置的另一個原因[用於決定是否刪除'd')。 –