为什么在使用std :: static_pointer_cast时std :: shared_ptr破坏两次?

时间:2020-02-23 07:07:25

标签: c++ casting shared-ptr

我的代码如下:

#include <iostream>
#include <memory>
#include <vector>
using namespace std;


struct A {
    A() { cout << "c"; }
    ~A() { cout << "d"; }
};

int main() {
    shared_ptr<void> a = make_shared<vector<A>>(3);
    auto b = static_pointer_cast<vector<A>>(a);
    b->push_back(A{});
    return 0;
}

它打印:

ccccdddddddd

表示析构函数称为两次。为什么会发生这种情况以及如何解决?

2 个答案:

答案 0 :(得分:2)

没有什么要修复的。每个对象不会两次调用析构函数。而是,您没有跟踪所有构造函数。将跟踪添加到副本并移动构造函数,如

struct A {
    A() { cout << "c"; }
    A(const A &) { cout << "C"; }
    A(A &&) { cout << "M"; }
    ~A() { cout << "d"; }
};

进行此更改后,您的输出应为

ccccMCCCdddddddd

表示8个构造函数调用和8个析构函数调用。

答案 1 :(得分:1)

析构函数调用是b->push_back(A{});而不是static_pointer_cast的结果

#include <iostream>
#include <memory>
#include <vector>
using namespace std;


struct A {
    A() { cout << "c"; }
    ~A() { cout << "d"; }
};

int main() {
    shared_ptr<void> a = make_shared<vector<A>>(3);
    auto b = static_pointer_cast<vector<A>>(a);

    return 0;
}

显示cccddd

之所以进行额外的析构函数调用,是因为矢量可能需要增加其在push_back上的容量,并且完成此操作后,它可能需要将现有元素移动/复制到新位置,并且删除旧元素。因此,您看到的其他析构函数就是这个结果。

在您的情况下,默认情况下已创建复制/移动构造。如果您手动定义它们并向其中添加日志,则可以看到它们匹配。

struct A {
    A() { cout << "c"; }
    A(const A&)  { cout << "C"; };
    A(A&&)  { cout << "M"; };
    ~A() { cout << "d"; }
};

防止这种情况的唯一方法是创建向量,使其具有足够大的容量以容纳要保存在其中的所有元素。