C++ 标准::向量不';t在重新分配时调用析构函数

C++ 标准::向量不';t在重新分配时调用析构函数,c++,memory,vector,destructor,C++,Memory,Vector,Destructor,我试图使用std::vector来保存一些S实例。但是,当我重新分配向量的成员时,不会对前一个租户调用析构函数: 密码 问题 因此,在位置1处的S(1)似乎永远不会被破坏。正如我在代码中指出的,我可以在重新分配析构函数之前手动调用它,但我不确定这是否是个好主意。是吗?如果不是,你有什么建议?还有,有吗 与我真正想要的联系 在真正的代码中,我在玩二叉树,我认为让节点成为向量的成员并通过向量中的索引相互指向会很有趣(让我获得连续内存缓存的好处,32位索引而不是64位指针,以及一些不同的东西)。但最终

我试图使用
std::vector
来保存一些
S
实例。但是,当我重新分配向量的成员时,不会对前一个租户调用析构函数:

密码 问题 因此,在位置
1
处的
S(1)
似乎永远不会被破坏。正如我在代码中指出的,我可以在重新分配析构函数之前手动调用它,但我不确定这是否是个好主意。是吗?如果不是,你有什么建议?还有,有吗

与我真正想要的联系
在真正的代码中,我在玩二叉树,我认为让节点成为向量的成员并通过向量中的索引相互指向会很有趣(让我获得连续内存缓存的好处,32位索引而不是64位指针,以及一些不同的东西)。但最终,我需要对树执行一些操作,这意味着移动/删除元素,因此我希望为删除的元素调用析构函数(我将使用
std::set
或一些东西来跟踪向量中的漏洞)。

分配给向量元素将调用复制分配操作符,不是复制构造函数。这是你需要的

struct S {
    int index_;
    S(int index) : index_(index) {
        std::cout << "Calling S " << index_ << " constructor\n";
    }
    S(const S& other) : index_(other.index_) {
        std::cout << "Calling S " << index_ << " copy constructor\n";
    }

    // added
    S& operator=(const S& other) {
        if (this == &other) { return *this; }
        std::cout << "Calling S " << index_ << " copy assignment\n";
        index_ = other.index_;
        return *this;
    }

    ~S() {
        std::cout << "Calling S " << index_ << " destructor\n";
    }
};

如果您的对象拥有某些资源,您会发现赋值运算符与析构函数和复制构造函数的操作类似。通过添加移动操作,可以将其扩展为规则5

赋值给向量元素将调用复制赋值操作符,而不是复制构造函数。这是你需要的

struct S {
    int index_;
    S(int index) : index_(index) {
        std::cout << "Calling S " << index_ << " constructor\n";
    }
    S(const S& other) : index_(other.index_) {
        std::cout << "Calling S " << index_ << " copy constructor\n";
    }

    // added
    S& operator=(const S& other) {
        if (this == &other) { return *this; }
        std::cout << "Calling S " << index_ << " copy assignment\n";
        index_ = other.index_;
        return *this;
    }

    ~S() {
        std::cout << "Calling S " << index_ << " destructor\n";
    }
};


如果您的对象拥有某些资源,您会发现赋值运算符与析构函数和复制构造函数的操作类似。通过添加移动操作,可以将其扩展为规则5

您忘记插入
操作符=
。赋值时不会调用析构函数(或者如果您愿意,也不会调用“重新赋值”)@Miles Budnek和move-construction/move-assignment(为了完整性)@JesperJuhl-True,但如果有用户提供的副本,则不会生成默认的move-constructor/assignment操作符,所以错过这些就不那么神秘了。@Miles Budnek我知道这一点。我的观点仅仅是为了完整性,如果OP后来还想测试在某些情况下,当类型是可移动的(比如如果调整对象向量的大小,如果move\u noexcept==true)时会发生什么。你知道,只是为了得到一个更完整的画面。你忘了插入
操作符=
。赋值时不调用析构函数(如果你愿意,也不调用“重新赋值”)@Miles Budnek和move construction/move assignment(为了完整性)…@JesperJuhl True,但是,如果有用户提供的副本,则不会生成默认的移动构造函数/赋值运算符,因此缺少这些运算符就不那么神秘了。@Miles Budnek我知道这一点。我的观点仅仅是为了完整性,如果OP后来还想测试在某些情况下,当类型是可移动的(比如如果调整对象向量的大小,如果move\u noexcept==true)时会发生什么。你知道,只是为了更全面地了解情况。被否决的选民应该明确地说明他们被否决的原因。你提到3和5的规则,可能为了完整性增加0的规则。@JesperJuhl。这与答案不太相关,但一定要谢谢你。最后一个例子真的做到了。反对者应该明确说明他们为什么反对。你提到3和5的规则,为了完整性,也许加上0的规则。@JesperJuhl。这与答案不太相关,但一定要谢谢你。最后一个例子真的做到了。
struct S {
    int index_;
    S(int index) : index_(index) {
        std::cout << "Calling S " << index_ << " constructor\n";
    }
    S(const S& other) : index_(other.index_) {
        std::cout << "Calling S " << index_ << " copy constructor\n";
    }

    // added
    S& operator=(const S& other) {
        if (this == &other) { return *this; }
        std::cout << "Calling S " << index_ << " copy assignment\n";
        index_ = other.index_;
        return *this;
    }

    ~S() {
        std::cout << "Calling S " << index_ << " destructor\n";
    }
};
int main() {
    S s1(1);
    s1 = S(2); // assignment, not destruction/construction. same idea
}