关于我没有得到的shared_ptr的东西

Something about shared_ptr I don't get

本文关键字:ptr shared 于我没      更新时间:2023-10-16

当我运行这段代码时:

#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->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->value = 7738248

为什么会这样?adapter->adaptee的use_count不是简单地从2更改为1,所以应该不会出错吗?当我删除'delete adapter;'这一行时,一切都很好,但是随后就有泄漏。

因为在第二种情况下,当您使用delete adapter时,Adaptee实例被shared_ptr的析构函数删除,因为它是拥有该对象的最后一个shared_ptr

如果您希望在删除适配器后保持适配器的活动,您应该从一开始就将其包装到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;
}

第一行

delete adapter;

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

在第二种情况下,同一行调用shared_ptr的析构函数,释放adaptee所指向的内存。因此,adaptee不是一个有效的指针,如果对它解引用,可能会出现未定义的行为。

shared_ptr的原理是管理不再使用的指向对象的删除。

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

如果你不希望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。

扩展其他人所说的,当您给shared_ptr一个原始堆分配的指针时,您给shared_ptr对资源生命周期的控制。shared_ptr保持一个引用计数,表示有多少shared_ptr指向一个资源。当该计数为0时,资源被清理。

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

这里引用计数是1,因为有1 shared_ptr指向资源a

delete adapter;

导致adapter中的shared_ptr超出作用域。调用shared_ptr的析构函数,减少引用计数。由于引用计数现在是0, deletea上被调用。

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

这里,a不再是一个有效的指针,因为deleteshared_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