为何C++ for循环中析构函数被多次调用?它销毁的是哪些对象?
C++ vector元素构造/析构异常问题解析
问题代码
#include <iostream> #include <vector> using namespace std; class Testing{ private: bool object_alive; int object_id; public: Testing(bool alive, int id); ~Testing(); bool check_if_alive(); void unalive(); }; Testing::Testing(bool alive, int id) : object_alive{alive}, object_id{id} { cout << "\nCONstructor on object: " << object_id << endl; } Testing::~Testing(){ unalive(); cout << "\nDEstructor on object: " << object_id << endl; } bool Testing::check_if_alive(){ return object_alive; } void Testing::unalive(){ object_alive = false; cout << "\nObject " << object_id << " under testing is dying!" << endl; } vector<Testing>* gather_objects_under_testing(int amount_of_objects); int main() { vector<Testing>* gas_chamber{gather_objects_under_testing(2)}; int number_of_objects_alive{0}; Testing* object_in_front{nullptr}; object_in_front = &(*gas_chamber).at((gas_chamber->size())-1); do{ number_of_objects_alive = 0; object_in_front->unalive(); object_in_front -= 1; for(auto &object: *gas_chamber){ if(object.check_if_alive()) ++number_of_objects_alive; } } while(number_of_objects_alive > 0); return 0; } vector<Testing>* gather_objects_under_testing(int amount_of_objects){ vector<Testing>* chamber = new vector<Testing>; for(int i{amount_of_objects}; i > 0; --i){ (*chamber).push_back(Testing(true, i)); } return chamber; }
运行输出
CONstructor on object: 2 Object 2 under testing is dying! DEstructor on object: 2 CONstructor on object: 1 Object 2 under testing is dying! DEstructor on object: 2 Object 1 under testing is dying! DEstructor on object: 1 Object 1 under testing is dying! Object 2 under testing is dying!
疑问解答与原因分析
1. gather_objects_under_testing中析构多次的原因
多次析构来自两个核心场景:
- 临时对象销毁:调用
push_back(Testing(true, i))时,会先创建一个临时Testing对象(触发构造),将其拷贝到vector内存后,临时对象立即被销毁(触发析构)。 - vector扩容导致旧元素销毁:vector初始容量为0,第一次
push_back会分配1个元素的空间;第二次push_back时,原容量不足,vector会重新分配更大的内存(通常是原容量的2倍),把旧内存中的元素拷贝到新内存,随后销毁旧内存中的元素,再次触发析构。
2. 被销毁的对象具体是哪些
- 第一次析构:
push_back(2)创建的临时对象; - 第二次析构:vector扩容时,旧内存中存储的对象2(拷贝到新内存后,旧对象被销毁);
- 第三次析构:
push_back(1)创建的临时对象; - 最后两次析构:程序退出时,vector中剩余的两个对象(1和2)被销毁(代码未手动
deletevector,属于内存泄漏,但操作系统回收内存前会触发析构)。
3. 为什么感觉修改的是"副本"
你在main中修改的是vector中最终存储的对象,但gather_objects_under_testing过程中,临时对象的unalive调用修改的是临时对象自身的object_alive,和vector中存储的拷贝对象无关——这就是你感知到"修改了未察觉的副本"的原因。
优化方案
方案1:提前预留vector容量
在gather_objects_under_testing中先调用reserve分配足够空间,避免扩容导致的元素拷贝和析构:
vector<Testing>* gather_objects_under_testing(int amount_of_objects){ vector<Testing>* chamber = new vector<Testing>; chamber->reserve(amount_of_objects); // 提前预留容量 for(int i{amount_of_objects}; i > 0; --i){ (*chamber).push_back(Testing(true, i)); } return chamber; }
方案2:使用emplace_back替代push_back
emplace_back直接在vector的内存空间中构造对象,无需创建临时对象,彻底避免临时对象的构造和析构:
vector<Testing>* gather_objects_under_testing(int amount_of_objects){ vector<Testing>* chamber = new vector<Testing>; chamber->reserve(amount_of_objects); for(int i{amount_of_objects}; i > 0; --i){ chamber->emplace_back(true, i); // 直接在vector内构造对象 } return chamber; }
方案3:避免裸指针管理vector
代码中new了vector但未delete,会造成内存泄漏。建议直接使用栈上vector或智能指针:
// 改用栈上vector,无需手动管理内存 vector<Testing> gather_objects_under_testing(int amount_of_objects){ vector<Testing> chamber; chamber.reserve(amount_of_objects); for(int i{amount_of_objects}; i > 0; --i){ chamber.emplace_back(true, i); } return chamber; } // main函数调整: int main() { vector<Testing> gas_chamber{gather_objects_under_testing(2)}; Testing* object_in_front = &gas_chamber.at(gas_chamber.size()-1); // 其余代码逻辑不变 }
总结
通过提前预留容量+使用emplace_back,可完全消除不必要的对象构造和析构;同时避免裸指针管理容器,能解决内存泄漏问题,提升代码安全性。
内容的提问来源于stack exchange,提问作者Mikołaj Łajming
相关产品推荐
相关产品推荐

