“删除此”到一个用std :: shared_ptr分配的对象?

时间:2012-04-22 19:12:47

标签: c++ memory-management c++11 shared-ptr

我知道只要使用传统指针分配delete this的内容,就可以在C ++中说new。事实上,我也知道如果你仔细处理它是很好的做法。如果某个对象是由delete this持有的话,我可以说std::shared_ptr吗?这应该叫析构函数,对吗?为了给你一个想法,我正在做一个船可以发射导弹的游戏,我想让导弹自己删除。

4 个答案:

答案 0 :(得分:12)

不,它不安全,对象的生命周期由shared_ptr的持有者决定,因此对象本身无法决定是否要死。如果你这样做,你会得到双倍的 上次shared_ptr死亡时删除。我能提供的唯一解决方案是“重新考虑你的设计”(你可能首先不需要shared_ptr,导弹可能是值或汇集对象)。

答案 1 :(得分: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';
    }
}

答案 2 :(得分:1)

这个问题已经很老了,但是我有一个类似的问题(在这种情况下是一个“监听器”对象,它必须管理自己的生命周期,同时仍然能够共享弱指针),并且谷歌搜索并没有提供解决方案我,所以我正在分享我找到的解决方案,假设:

  • 该对象管理它自己的生命周期,因此永远不会共享一个 share_ptr,但是一个weak_ptr(如果你需要shared_ptr类似的话 解决方案+ use_shared_from_this就可以了。)
  • 打破RAII是一个坏主意,因此我们不会这样做:我们是什么 这里的地址是拥有一个对象拥有的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删除器,因此当它在正常的对象销毁时,它只是绕过删除(我们到目前为止,因为已经调用了默认对象析构函数)。

答案 3 :(得分:0)

我知道我已经迟到了,但我现在想要自己做这件事,并意识到这是“有可能的”,但你需要照顾好一些事情。

霍华德的答案是在正确的轨道上,但是错过了标记,因为你不应该将原始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()将在语义上产生与delete this相同的效果,并带来额外的好处,即所有引用已删除对象的MissilePtr都将过期。此外,如果MissilePtr中的任何this用于访问std::shared_ptr,则删除将被延迟,直到用于访问的临时MissilePtr被销毁,从而为您节省生命时间的麻烦。

但是,您必须确保始终保持至少一个die(),并在某个时刻调用{{1}},否则您将最终导致内存泄漏。就像你使用普通指针一样。