C++类构造函数内调用成员类构造函数的可行性及最佳实践
问题原因
你的代码写法本身存在逻辑错误:类的非静态成员变量会在进入构造函数体之前就完成默认初始化,你在构造函数体内部写的MainObject("something", 1, 2)本质是创建了一个临时的MainClass对象,执行完该行代码就会被销毁,完全不会作用到类的成员变量上,自然只会在局部生效。
可行方案(按推荐优先级排序)
方案1:初始化列表+工厂函数(性能最优、扩展性最好)
把不同分支的初始化逻辑封装到独立的工厂函数里,直接放到初始化列表完成成员构造,不需要执行MainClass的默认构造,也不需要额外的赋值操作,非常适配你提到的「不同分支初始化逻辑差异极大」的场景:
// ContainerObject.cpp 开头,写在匿名命名空间里对外不可见 namespace { MainClass createMainObject(int type) { if (type == 0) { // 可以写任意复杂的初始化逻辑 return MainClass("something", 1, 2); } else if (type == 1) { // 不同分支的逻辑完全独立 return MainClass(4, "another thing", "yet another thing"); } // 可以加默认分支的异常处理/默认初始化逻辑 throw std::invalid_argument("invalid type"); } } // 构造函数直接用工厂函数的返回值初始化成员 ContainerObject::ContainerObject(int type) : MainObject(createMainObject(type)) { // 其他构造逻辑 }
方案2:移动/拷贝赋值(改动最小)
如果MainClass支持移动赋值(C++11及以后版本符合条件的类会默认生成移动赋值运算符),直接给成员变量赋值即可,代码改动量最小:
ContainerObject::ContainerObject(int type) { if (type == 0){ MainObject = MainClass("something", 1, 2); } else if (type == 1){ MainObject = MainClass(4, "another thing", "yet another thing"); } }
如果MainClass不支持移动,支持拷贝赋值也可以用这个方案,只是性能会稍差一点。
方案3:std::optional包装成员(C++17及以上)
如果MainClass既不支持移动也不支持拷贝赋值,也不想改初始化逻辑,可以用std::optional包装成员,完全控制成员的构造时机:
// ContainerObject.hpp #include <optional> class ContainerObject { public: std::optional<MainClass> MainObject; ContainerObject(int type); }; // ContainerObject.cpp ContainerObject::ContainerObject(int type) { if (type == 0){ MainObject.emplace("something", 1, 2); } else if (type == 1){ MainObject.emplace(4, "another thing", "yet another thing"); } }
后续访问成员的时候加*或者->即可,没有堆分配开销。
方案4:堆分配(仅迫不得已使用)
就是你提到的用指针存储成员的方案,会有额外的堆分配开销,仅适合MainClass是不可移动、不可拷贝、也不能用std::optional包装的特殊类型:
// ContainerObject.hpp #include <memory> class ContainerObject { public: std::unique_ptr<MainClass> MainObject; ContainerObject(int type); }; // ContainerObject.cpp ContainerObject::ContainerObject(int type) { if (type == 0){ MainObject = std::make_unique<MainClass>("something", 1, 2); } else if (type == 1){ MainObject = std::make_unique<MainClass>(4, "another thing", "yet another thing"); } }
关于你提到的多构造函数委托方案
这个方案也可以实现,但是只适合分支数少、参数组合固定的场景,如果分支多、初始化逻辑复杂,代码会变得非常冗余难维护,示例实现如下:
// ContainerObject.hpp class ContainerObject { public: MainClass MainObject; ContainerObject(int type); private: // 私有构造对应不同初始化分支 ContainerObject(std::string s, int a, int b): MainObject(std::move(s), a, b) {} ContainerObject(int a, std::string s1, std::string s2): MainObject(a, std::move(s1), std::move(s2)) {} }; // ContainerObject.cpp ContainerObject::ContainerObject(int type): ContainerObject( type == 0 ? ContainerObject("something", 1, 2) : ContainerObject(4, "another thing", "yet another thing") ) {}
内容的提问来源于stack exchange,提问作者Gaston
相关产品推荐
相关产品推荐

