关于shared_ptr的一些事情我不明白

时间:2014-08-22 16:03:53

标签: c++ shared-ptr

当我运行此代码时:

#include <iostream>
#include <memory>
struct Adaptee {
    int value = 0;
};

class Adapter {
    private:
        Adaptee* adaptee;
    public:
        Adapter (Adaptee* a) : adaptee(a) {}
        void increaseValueByOne() {adaptee->value++;}
};

int main() {
    Adaptee* a = new Adaptee;
    Adapter* adapter = new Adapter(a);
    adapter->increaseValueByOne();  
    delete adapter;
    std::cout << "a->value = " << a->value << std::endl;
}

我得到输出:a-&gt; value = 1.但是当我运行时:

struct Adaptee {
    int value = 0;
};

class Adapter {
    private:
        std::shared_ptr<Adaptee> adaptee;
    public:
        Adapter (Adaptee* a) : adaptee (std::shared_ptr<Adaptee>(a)) {}
        void increaseValueByOne() {adaptee->value++;}
};

int main() {
    Adaptee* a = new Adaptee;
    Adapter* adapter = new Adapter(a);
    adapter->increaseValueByOne();
    delete adapter;
    std::cout << "a->value = " << a->value << std::endl;
}

我得到:a-&gt; value = 7738248

这是为什么?不是适配器&gt;适配器的use_count只是从2变为1,所以什么都不应该出错?当我删除行'删除适配器;'一切都很好,但随后出现了泄漏。

4 个答案:

答案 0 :(得分:4)

因为在第二种情况下Adaptee实例被shared_ptr的析构函数删除delete adapter,因为它是拥有该对象的最后一个shared_ptr。请参阅the doc

如果你想在删除适配器后让适配器保持活动状态,你应该从头开始将它包装到shared_ptr中:

class Adapter {
    private:
        std::shared_ptr<Adaptee> adaptee;
    public:
        Adapter (const std::shared_ptr<Adaptee>& a) : adaptee (a) {}
        void increaseValueByOne() {adaptee->value++;}
};

int main() {
    std::shared_ptr<Adaptee> a(new Adaptee);
    Adapter* adapter = new Adapter(a);
    adapter->increaseValueByOne();
    delete adapter;
    std::cout << "a->value = " << a->value << std::endl;
}

答案 1 :(得分:2)

在第一种情况下,行

delete adapter;

adaptee没有任何作用。它仍然是一个有效的指针。

在第二种情况下,同一行调用shared_ptr的析构函数,该析构函数释放adaptee指向的内存。因此,adaptee不是一个有效的指针,如果你取消引用它,你可以期待未定义的行为。

答案 2 :(得分:1)

shared_ptr的原则是在不再使用尖头对象时管理它的删除。

当您create适配器中的shared_ptr时,它将获取对象的所有权并将使用计数设置为1.删除适配器时,共享指针也是如此。因此,使用计数(您可以使用use_count()显示)会自动递减并达到零。因此删除对象并指向无效地址。

如果你没有&#39;希望shared_ptr在删除adapter时释放a的内存,你也应该将你的原始指针创建为shared_pointer:

int main() {
    auto a = std::make_shared<Adaptee>();   //shared pointer instead of Adaptee* a = new Adaptee;
    ...
    }

在类Adapter中添加一个额外的构造函数:

Adapter(std::shared_ptr<Adaptee> a) : adaptee(a) {}

通过shared_ptr的这种一致使用,当删除适配器时,共享指针的使用次数将变为1,其余代码将按照您的要求运行,与原始版本一样打印1。

答案 3 :(得分:1)

要扩展其他人所说的内容,当您为原始堆分配指向shared_ptr的指针时,您将对资源的生命周期进行shared_ptr控制。 shared_ptr保留指向资源的shared_ptr个数的引用计数。当该计数为0时,资源将被清理。

adaptee (std::shared_ptr<Adaptee>(a))

此处,引用计数为1,因为1 shared_ptr指向资源a

delete adapter;

导致shared_ptr中的adapter超出范围。调用shared_ptr的析构函数,它会减少引用计数。由于引用计数现在为0,因此delete会调用a

std::cout << "a->value = " << a->value << std::endl;

这里,a不再是有效指针,因为在delete的析构函数中调用了shared_ptr

如果您希望a保持有效,则应执行以下操作:

struct Adaptee {
    int value = 0;
};

class Adapter {
    private:
        std::shared_ptr<Adaptee> adaptee;
    public:
        Adapter (std::shared_ptr<Adaptee> a) : adaptee(a) {}
        void increaseValueByOne() {adaptee->value++;}
};

int main() {
    std::shared_prt<Adaptee> a = std::make_shared<Adaptee>(); //reference count is 1
    Adapter* adapter = new Adapter(a); //reference count is 2
    adapter->increaseValueByOne();
    delete adapter; //reference count is 1
    std::cout << "a->value = " << a->value << std::endl;
} // reference count is 0; memory is deallocated