2012-04-22 64 views
4

我知道使用傳統的指針可以在C++中使用new來分配delete this。事實上,我也知道如果你仔細處理,這是一個很好的做法。如果某個對象被std::shared_ptr保存,是否可以有對象delete this?那應該稱爲析構函數,對吧?爲了給你一個想法,我正在做一個遊戲,在那裏一艘船可以發射導彈,我想讓導彈自己刪除。「刪除此」到一個分配了std :: shared_ptr的對象?

+3

使用'delete this'刪除對象本身不是很好的做法。事實上,你必須小心處理,足以證明這一點。這是安排生命週期管理的一種容易出錯的方式。 – bames53 2012-04-23 03:06:20

回答

12

不,這是不安全的,對象的生命週期由shared_ptr的持有者決定,因此對象本身不能決定它是否想死。如果你這樣做,當最後一個shared_ptr死亡時,你會得到兩倍的 刪除。我能提供的唯一解決方案是「重新考慮你的設計」(你可能首先不需要shared_ptr,而導彈可能是價值或集中對象)。

3

對於導彈要自行刪除,它必須擁有自己,或者至少與其他人分享自己的所有權。既然你說這個導彈有一個shared_ptr,我假設你已經有多個物體共享導彈的所有權。

導彈有可能持有自己的shared_ptr,從而分享自己的所有權。但是,這總是會產生一個週期性的所有權模式:只要導彈的shared_ptr數據成員指向自身,參考數就不會降到零,因此導彈就會泄漏。

你可以有一個外部對象或事件告訴導彈刪除自己,但然後我不知道重點是什麼。爲了讓導彈自行刪除,該通信應通過shared_ptr進行,然後在shared_ptr放下導彈之前刪除並不會真的發生。

是的,這是可能的。不,我認爲這不是一個好主意。它看起來容易發生內存泄漏,實際上並沒有增加價值。但對於好奇,這裏是你會怎麼做:

#include <iostream> 
#include <memory> 

class missile 
    : public std::enable_shared_from_this<missile> 
{ 
    std::shared_ptr<missile> self_; 
public: 
    missile() 
     {} 

    ~missile() {std::cout << "~missile()\n";} 

    void set_yourself() 
    { 
     self_ = shared_from_this(); 
    } 
    void delete_yourself() 
    { 
     if (self_) 
      self_.reset(); 
    } 
}; 

int main() 
{ 
    try 
    { 
     std::shared_ptr<missile> m = std::make_shared<missile>(); 
     m->set_yourself(); 
     std::weak_ptr<missile> wp = m; 
     std::cout << "before first reset()\n"; 
     m.reset(); 
     std::cout << "after first reset()\n"; 
     // missile leaked here 
     m = wp.lock(); 
     m->delete_yourself(); 
     std::cout << "before second reset()\n"; 
     m.reset(); // missile deleted here 
     std::cout << "after second reset()\n"; 
    } 
    catch (const std::exception& e) 
    { 
     std::cout << e.what() << '\n'; 
    } 
} 
+0

我不確定我是否應該投票。解決方案是正確和新穎的,但問題是錯誤的。 – 2012-04-23 17:24:12

+0

怎麼有內存泄漏? 'wp'沒有過期。您在處理原始指針時的責任完全相同。如果你碰巧在使用導彈的同時刪除自己,你將不會從地板下面拔出地毯。 – 2014-10-04 12:49:02

+0

@EmilyL .:我說*容易發生內存泄漏。如果有人忽略了告訴導彈要自行刪除,那麼它就會泄漏。這個例子演示了通過刪除所有外部所有者而不調用'delete_yourself'。這個例子然後重新建立一個外部所有者,告訴導彈現在自己刪除,然後在外部所有者放棄所有權時被刪除。即爲什麼不只是有外部所有者呢?一個更常見的習慣用法是,導彈只能自己維護一個'weak_ptr',並使用'weak_ptr'將強大的所有權交給外部實體。 – 2014-10-04 17:08:56

0

我知道我遲到的演出,但我遇到了想剛纔這個做我自己,並意識到它是「一種-OF-可能的「,但你需要照顧一些事情。

霍華德的答案是正確的軌道,但錯過了商標,因爲你不應該把原來的shared_ptr的建設留給客戶。這是開放內存泄漏的風險。相反,你應該封裝結構,只允許弱指針。

下面是一個例子:

class Missile{ 
private: 
    Missile(...){ }; // No external construction allowed 
    Missile(const Missile&) = delete; // Copying not allowed 
    void operator = (const Missile&) = delete; // -||- 

    std::shared_ptr<Missile> m_self; 
public: 
    template<typename... Args> 
    static MissilePtr makeMissile(Args... args){ 
     auto that = std::make_shared<Misile>(args...); 
     that.m_self = that; // that holds a reference to itself (ref count = 2) 
     return that; // 'that' is destroyed and ref-count reaches 1. 
    } 

    void die(){ 
     m_self.reset(); 
    } 

    ... 
}; 

typedef std::weak_ptr<Missile> MissilePtr; 

void useMissile(MissilePtr ptr){ 
    auto missile = ptr.lock(); // Now ptr cannot be deleted until missile goes out of scope 
    missile->die(); // m_self looses the reference but 'missile' still holds a reference 
    missile->whatever(); // Completely valid. Will not invoke UB 
} // Exiting the scope will make the data in missile be deleted. 

調用die()將導致與所有MissilePtr被參考刪除對象將過期的附加益處爲delete this語義相同的效果。同樣,如果使用MissilePtr中的任何一個來訪問this,那麼刪除將被延遲,直到用於訪問的臨時std::shared_ptr被銷燬,從而可以避免生命中的頭痛。

但是,您必須確保始終至少保留一個MissilePtr,並在某個時間調用die()否則您將最終發生內存泄漏。就像你用普通的指針一樣。

1

這個問題已經很老了,但我有類似的問題(在這種情況下,一個「偵聽器」對象必須管理自己的生命週期,同時仍然能夠共享弱指針),並且使用Google搜索並未提供解決方案對我來說,讓我分享我找到了解決辦法,假設:

  • 對象管理它自己的生命週期,因此決不會共享一個 share_ptr,但weak_ptr的(如果你需要shared_ptr的是一個類似 解決方案+ use_shared_from_this可以做到這一點)。
  • 這是一個壞主意,打破RAII,因此我們不會做到這一點:我們 ADRESS這裏是具有由對象 本身擁有一個shared_ptr,含有成員share_ptr的問題導致對 兩個人通話(或者至少未定義的行爲),因爲析構函數被調用兩次(一次在正常的 對象銷燬,另一次在銷燬包含shared_ptr成員的自我 時)。

代碼:

#include <memory> 
#include <stdio.h> 

using std::shared_ptr; 
using std::weak_ptr; 

class A { 
    struct D { 
      bool deleted = false; 
      void operator()(A *p) { 
       printf("[deleter (%s)]\n", p, deleted ? "ignored":"deleted"); 
       if(!deleted) delete p; 
     } 
    }; 

    public: shared_ptr<A> $ptr = shared_ptr<A>(this, D()); 

    public: ~A() { 
     std::get_deleter<A::D>($ptr)->deleted = true; 
    } 

    public: weak_ptr<A> ptr() { return $ptr; } 
}; 

void test() { 
    A a; 

    printf("count: %d\n", a.ptr().lock().use_count()); 
    printf("count: %d\n", a.ptr().use_count()); 
} 

int main(int argc, char *argv[]) { 
    puts("+++ main"); 

    test(); 

    puts("--- main"); 
} 

輸出:

$ g++ -std=c++11 -o test test.cpp && ./test 
+++ main 
count: 2 
count: 1 
[deleter (ignored)] 
--- main 

shared_ptr的缺失者應該永遠不會要求在堆棧中分配一個對象,所以當它在普通對象的破壞,它只是繞過刪除(我們到了這一點,因爲默認對象析構函數已被調用)。

相關問題