C++中vector使用push_back(std::move)出现额外析构函数调用问题咨询
为什么使用std::move后析构函数调用次数远超预期?
我写了一段C++代码,原本预期析构函数只会被调用2次,但实际运行时却调用了4次,输出结果和预期不符,想知道哪里出错了。
我的代码如下:
#include <iostream> #include <vector> #include <algorithm> class my { public: my() { counter++; std::cout << "class constructor" << counter << " \n";} ~my() { std::cout << "class destructor" << counter << " \n"; counter--; } static inline int counter = 0; }; int main() { my v1; std::vector<my> my_vec; my * p = new my(); my_vec.push_back(std::move(*p)); my_vec.push_back(std::move(v1)); }
实际运行输出:
class constructor1
class constructor2
class destructor2
class destructor1
class destructor0
class destructor-1
问题根源分析
咱们一步步拆解代码里的对象生命周期和操作,就能理清为什么析构次数会超出预期:
1. 你忽略了vector扩容带来的析构
当你第二次调用push_back时,vector的初始容量(通常是1)不足以容纳第二个元素,会触发扩容操作:
- 先分配一块更大的新内存;
- 把vector里已有的那个对象移动到新内存;
- 析构旧内存里的原对象——这就是你没预料到的第一次额外析构,对应输出里的
class destructor2。
2. 编译器生成的默认移动构造没更新你的静态计数器
你的类没有自定义移动构造函数,编译器会生成一个默认版本。这个默认移动构造不会调用你的构造函数(也就不会执行counter++),但析构函数依然会执行counter--。这就导致你的counter数值和实际存在的对象数量完全不匹配,看起来像是析构次数“多了”,其实是计数器的统计逻辑本身有问题。
3. 堆对象没有被delete,存在内存泄漏
你用new创建了*p,但全程没调用delete p,这个堆对象的析构函数永远不会被调用——它的生命周期不会随main结束而自动结束,操作系统只会回收内存,但不会执行C++的析构逻辑。
修正后的代码示例
要解决这些问题,我们需要补全移动构造函数、预分配vector容量避免扩容,同时记得释放堆对象:
#include <iostream> #include <vector> #include <algorithm> class my { public: my() { counter++; std::cout << "default constructor, counter: " << counter << "\n"; } // 补全拷贝构造(如果需要的话) my(const my&) { counter++; std::cout << "copy constructor, counter: " << counter << "\n"; } // 自定义移动构造,明确计数器逻辑(这里因为类无资源,只是做统计) my(my&&) noexcept { counter++; std::cout << "move constructor, counter: " << counter << "\n"; } ~my() { std::cout << "destructor, counter: " << counter << "\n"; counter--; } static inline int counter = 0; }; int main() { my v1; std::vector<my> my_vec; // 预分配容量,避免扩容 my_vec.reserve(2); my *p = new my(); my_vec.push_back(std::move(*p)); // 释放堆对象 delete p; my_vec.push_back(std::move(v1)); }
这段代码运行时,你能清晰看到每个构造、移动、析构的调用过程,计数器也能准确反映对象数量的变化,同时不会有额外的扩容析构和内存泄漏。
内容的提问来源于stack exchange,提问作者GregK1
相关产品推荐
相关产品推荐

