C++链式调用add()失效:子对象丢失及对象复制析构问题求助
C++链式调用add()方法丢失子对象问题排查
问题描述
编写了一段C++代码,期望通过A类的add()方法链式调用构建指定对象结构,但实际运行后仅最后一个子对象的子元素存在,前面的子对象的子元素丢失,控制台日志显示存在对象复制与销毁的情况。
代码实现
#include <inttypes.h> #include <stdio.h> #include <string> #include <vector> #include <iostream> class A { static int obj_cnt; public: int id = 0; std::string ostr; std::vector<A> chlds; A(const A& _a) { ostr = _a.ostr; id = _a.id; chlds = _a.chlds; printf("\n A(const A&) : obj_cnt = %d, id = %d, name = %s", A::obj_cnt, id, ostr.c_str()); } A(const char* str) : ostr(str) { id = A::obj_cnt++; printf("\n A(const char*): obj_cnt = %d, id = %d, name = %s", A::obj_cnt, id, ostr.c_str()); } A& add(const char* str) { chlds.emplace_back(str); printf("\nin %s, chlds.size = %d", ostr.c_str(), chlds.size()); return chlds.back(); } ~A() { printf("\n~A(). id = %d, name = %s", id, ostr.c_str()); } }; int A::obj_cnt = 0; int main() { A obj("Container_1"); obj.add("a").add("xa"); obj.add("b").add("xb"); obj.add("c").add("xc"); }
控制台日志
A(const char*): obj_cnt = 1, id = 0, name = Container_1 A(const char*): obj_cnt = 2, id = 1, name = a in Container_1, chlds.size = 1 A(const char*): obj_cnt = 3, id = 2, name = xa in a, chlds.size = 1 A(const char*): obj_cnt = 4, id = 3, name = b A(const A&) : obj_cnt = 4, id = 2, name = xa A(const A&) : obj_cnt = 4, id = 1, name = a ~A(). id = 1, name = a ~A(). id = 2, name = xa in Container_1, chlds.size = 2 A(const char*): obj_cnt = 5, id = 4, name = xb in b, chlds.size = 1 A(const char*): obj_cnt = 6, id = 5, name = c A(const A&) : obj_cnt = 6, id = 2, name = xa A(const A&) : obj_cnt = 6, id = 1, name = a A(const A&) : obj_cnt = 6, id = 4, name = xb A(const A&) : obj_cnt = 6, id = 3, name = b ~A(). id = 1, name = a ~A(). id = 2, name = xa ~A(). id = 3, name = b ~A(). id = 4, name = xb in Container_1, chlds.size = 3 A(const char*): obj_cnt = 7, id = 6, name = xc in c, chlds.size = 1
预期结果
obj.chlds = [A("a"), A("b"), A("c")], obj.chlds[0].chlds = [A("xa")] obj.chlds[1].chlds = [A("xb")], obj.chlds[2].chlds = [A("xc")]
实际结果
obj.chlds[0] = [A("a")] obj.chlds[1] = [A("b")] obj.chlds[2] = [A("c")], obj.chlds[0].chlds[0] = [], 但应包含A("xa") obj.chlds[1].chlds[0] = [], 但应包含A("xb") obj.chlds[2].chlds[0] = [A("xc")]
问题分析
核心原因:std::vector的扩容机制
std::vector在容量不足时会自动扩容:分配更大的内存空间,将现有元素复制到新内存区域,然后销毁旧内存中的元素。
在你的代码中:
- 调用
obj.add("a").add("xa")时,成功给obj.chlds[0](A("a"))添加了子对象A("xa")。 - 调用
obj.add("b")时,obj.chlds初始容量不足(默认初始容量通常为1),触发扩容:- 复制旧的A("a")及其子对象A("xa")到新内存
- 销毁旧的A("a")和A("xa")
- 此时
obj.chlds[0]是新复制的A("a"),其chlds中包含复制后的A("xa")
- 调用
obj.add("c")时,obj.chlds容量再次不足,触发第二次扩容:- 复制当前的A("a")、A("b")及其子对象到新内存
- 销毁旧的A("a")、A("xa")、A("b")、A("xb")
你看到的对象复制和销毁日志,就是vector扩容时的正常行为。
为何子对象看似丢失?
你观察到的obj.chlds[0].chlds为空,大概率是调试时的误解:
- 你可能查看了已经被销毁的旧对象的内存地址,而非扩容后新复制的对象
- 拷贝构造函数中
id直接沿用原对象的id,导致调试时多个对象id重复,混淆了新旧对象
实际上,扩容后的新对象的chlds中是包含子对象拷贝的,只是旧对象被销毁了。
解决方案
方案1:提前预留vector容量
在创建obj后,提前给obj.chlds预留足够的容量,避免扩容:
int main() { A obj("Container_1"); obj.chlds.reserve(3); // 预留3个元素的容量,添加a/b/c时不会触发扩容 obj.add("a").add("xa"); obj.add("b").add("xb"); obj.add("c").add("xc"); }
这样obj.chlds不会触发扩容,也就不会有对象复制和销毁,所有子对象都会保留在原位置。
方案2:使用指针存储子对象
将chlds的类型改为std::vector<std::unique_ptr<A>>,存储对象指针而非对象本身。扩容时仅复制指针,不会复制或销毁对象:
#include <inttypes.h> #include <stdio.h> #include <string> #include <vector> #include <memory> #include <iostream> class A { static int obj_cnt; public: int id = 0; std::string ostr; std::vector<std::unique_ptr<A>> chlds; A(const A& _a) = delete; // 禁用拷贝构造,避免指针拷贝问题 A& operator=(const A&) = delete; A(const char* str) : ostr(str) { id = A::obj_cnt++; printf("\n A(const char*): obj_cnt = %d, id = %d, name = %s", A::obj_cnt, id, ostr.c_str()); } A& add(const char* str) { chlds.emplace_back(std::make_unique<A>(str)); printf("\nin %s, chlds.size = %d", ostr.c_str(), chlds.size()); return *chlds.back(); } ~A() { printf("\n~A(). id = %d, name = %s", id, ostr.c_str()); } }; int A::obj_cnt = 0; int main() { A obj("Container_1"); obj.add("a").add("xa"); obj.add("b").add("xb"); obj.add("c").add("xc"); }
这种方式彻底避免了对象的拷贝和销毁,同时保留链式调用的能力。
内容的提问来源于stack exchange,提问作者user003
相关产品推荐
相关产品推荐

