【问题标题】:Why std::shared_ptr destruct twice when used std::static_pointer_cast?为什么 std::shared_ptr 在使用 std::static_pointer_cast 时会破坏两次?
【发布时间】:2020-02-23 07:07:25
【问题描述】:

我的代码如下:

#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

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

【问题讨论】:

  • “插桩”成员函数时,打印this的值

标签: c++ casting shared-ptr


【解决方案1】:

没有什么可以解决的。每个对象不会调用析构函数两次。相反,您没有跟踪所有构造函数。向复制和移动构造函数添加跟踪,如

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

有了这个改变,你的输出应该是

ccccMCCCdddddddd

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

【讨论】:

    【解决方案2】:

    析构函数调用是b-&gt;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"; }
    };
    

    防止它的唯一方法是创建一个容量足够大的向量,以容纳您想要保存的所有元素。

    【讨论】:

    • 在重新分配向量空间以增加其容量时,为什么不调用ctor而调用dtor?
    • @XiaodongLiu 因为在移动复制或移动构造函数时被调用。如果您不自己定义它们,则这些是(如果可能的话)默认创建的。
    猜你喜欢
    • 1970-01-01
    • 2017-05-02
    • 1970-01-01
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-06
    相关资源
    最近更新 更多