共享指针的析构函数和擦除

问题描述

据我所知,当从容器中擦除元素时会调用析构函数,如果我错了,请纠正我..但是 Destr X 打印不应该出现在 擦除之后

#include <iostream>
#include <memory>
#include <vector>

struct X 
{
    X()
    {
        std::cout<<"Contr X"<<std::endl;
    }
    ~X()
    {
        std::cout<<"Destr X"<<std::endl;
    }
};

using Xptr = std::shared_ptr<X>;

int main()
{
    
    std::shared_ptr<X> x = std::make_shared<X>();
    Xptr x2 = std::make_shared<X>();
    std::vector<Xptr> v;
    v.emplace_back(x);
    v.emplace_back(x2);
    v.erase(v.begin());
    std::cout<<"after erase\n";
    return 0;
}

解决方法

由于您使用了 shared_ptr,因此您对这两个对象都有两个引用。它们是 xx2,以及向量 shared_ptr 内的 v

当你erase()时,调用了shared_ptr的析构函数,并且引用计数递减,但仍然是1。因此对象 X 仍然存在。 离开main()的范围后,计数再次递减。现在计数下降到零,并调用对象 X 的析构函数。

因此首先显示“擦除后”的输出,然后是“Destr X”。


当您将 xx2 放入内部作用域时,您将看到预期的行为

std::vector<Xptr> v;

{
    std::shared_ptr<X> x = std::make_shared<X>();
    Xptr x2 = std::make_shared<X>();
    v.emplace_back(x);
    v.emplace_back(x2);
}

std::cout<<"before erase\n";
v.erase(v.begin());
std::cout<<"after erase\n";

现在你看到了

Contr X
Contr X
before erase
Destr X
after erase
Destr X
,

“据我所知,当从容器中删除元素时会调用析构函数......”

没错。但是当该元素是 std::shared_ptr 时,它是 std::shared_ptr 的析构函数被调用,而不是 std::shared_ptr 指向的任何析构函数。

std::shared_ptr调用它指向的对象的析构函数,如果它是最后 std::shared_ptr 指向它。但是在您的代码中,您有 2 std::shared_ptr 指向您创建的每个对象。 std::vector 中各有一个,本地 std::shared_ptr 变量 xx2 各有一个,然后将它们添加到 std::vector

因此,当您从 std::vector 中删除元素时,std::vector 会删除它包含的 std::shared_ptr,但是这些 std::shared_ptr 指向的对象不会被销毁,直到std::shared_ptr 变量 xx2main() 末尾超出范围。