C++类成员变量疑似重复初始化?首次添加的Bar对象丢失问题
Foo实例首次添加Bar丢失的原因与修复方案
问题描述
实现了Bar和Foo两个C++类:
- Bar类包含字符串成员
info - Foo类通过静态
vector<Foo> infoStore存储实例,每个Foo实例持有vector<Bar> bars
调用时发现每个Foo对象首次添加的Bar(如info11、info21)并未被保存,后续添加的Bar则正常,即使在构造函数显式初始化bars也无法解决该问题。
相关代码
Bar.hpp
#include <string> class Bar { public: Bar(const std::string& info); std::string getInfo() { return info; } protected: private: std::string info; };
Bar.cpp
#include <Bar.hpp> Bar::Bar(const std::string& info) : info(info) { }
Foo.hpp
#include <string> #include <vector> #include <Bar.hpp> class Foo { public: static void printInfoStore(); static Foo* getFoo(const std::string& name); Foo() {}; std::string getName() { return name; } std::vector<Bar> getBars(); void addBar(const std::string& info); protected: private: static std::vector<Foo> infoStore; Foo(const std::string& name); std::vector<Bar> bars{}; std::string name; };
Foo.cpp
#include <iostream> #include <Foo.hpp> Foo::Foo(const std::string& name) : name(name) { } // static std::vector<Foo> Foo::infoStore(0); // static void Foo::printInfoStore() { std::cout << "InfoStore is { "; for (Foo& foo : infoStore) { std::cout << foo.getName() << "=[ "; for (Bar& bar : foo.getBars()) { std::cout << bar.getInfo() << " "; } std::cout << "] "; } std::cout << " }" << std::endl; } // static Foo* Foo::getFoo(const std::string& name) { for (Foo& foo : infoStore) { if (foo.getName() == name) { return &foo; } } Foo* foo = new Foo(name); infoStore.push_back(*foo); return foo; } std::vector<Bar> Foo::getBars() { return bars; } void Foo::addBar(const std::string& info) { Bar* bar = new Bar(info); bars.push_back(*bar); }
main.cpp
#include <Foo.hpp> #include <Bar.hpp> int main(int argc, char *argv[]) { Foo::printInfoStore(); Foo::getFoo("Foo1")->addBar("info11"); // info11 is not added to Foo1 Foo::printInfoStore(); Foo::getFoo("Foo1")->addBar("info12"); Foo::printInfoStore(); Foo::getFoo("Foo1")->addBar("info13"); Foo::printInfoStore(); Foo::getFoo("Foo2")->addBar("info21"); // info21 is not added to Foo2 Foo::printInfoStore(); Foo::getFoo("Foo2")->addBar("info22"); Foo::printInfoStore(); return 0; }
程序输出
InfoStore is { } InfoStore is { Foo1=[ ] } InfoStore is { Foo1=[ info12 ] } InfoStore is { Foo1=[ info12 info13 ] } InfoStore is { Foo1=[ info12 info13 ] Foo2=[ ] } InfoStore is { Foo1=[ info12 info13 ] Foo2=[ info22 ] }
原因分析
核心问题出在getFoo函数的逻辑:
- 创建新Foo实例时,先通过
new Foo(name)在堆上分配一个对象 - 调用
infoStore.push_back(*foo)时,会将堆上Foo对象的副本存入vector - 函数返回的是堆上Foo对象的指针,而非infoStore中副本的指针
首次调用addBar是给堆上的Foo对象添加Bar,但这个堆对象并没有被存入infoStore;第二次调用getFoo时,会找到infoStore里的副本并返回其指针,后续的addBar都是操作这个副本,所以首次添加的Bar不会出现在infoStore的实例中,也就不会被打印出来。
代码存在的问题
getFoo逻辑错误:堆对象与infoStore中的实例是两个独立对象,导致首次修改的是未被存储的堆对象getBars返回值为值拷贝:每次调用都会复制整个bars向量,既浪费性能,也无法通过返回值修改原对象的barsaddBar存在内存泄漏:通过new创建Bar对象后未delete,导致内存无法回收- 无参构造函数为public权限:可能意外创建未初始化
name的无效Foo实例
修复方案
1. 修正getFoo函数逻辑
直接在infoStore中构造Foo实例,避免副本问题:
Foo* Foo::getFoo(const std::string& name) { for (Foo& foo : infoStore) { if (foo.getName() == name) { return &foo; } } // 直接在vector末尾构造Foo实例,避免拷贝 infoStore.emplace_back(name); // 返回vector中最后一个元素的指针 return &infoStore.back(); }
2. 修改getBars返回引用
避免不必要的拷贝,同时保证访问的是原对象的bars:
在Foo.hpp中修改声明:
std::vector<Bar>& getBars();
在Foo.cpp中修改实现:
std::vector<Bar>& Foo::getBars() { return bars; }
3. 修复addBar的内存泄漏
直接在bars中构造Bar对象,无需堆分配:
void Foo::addBar(const std::string& info) { bars.emplace_back(info); }
4. 限制无参构造函数权限
将无参构造函数设为private,防止意外创建无效实例:
class Foo { // ... 其他成员 ... private: Foo() {}; // ... 其他私有成员 ... };
修复后程序输出
InfoStore is { } InfoStore is { Foo1=[ info11 ] } InfoStore is { Foo1=[ info11 info12 ] } InfoStore is { Foo1=[ info11 info12 info13 ] } InfoStore is { Foo1=[ info11 info12 info13 ] Foo2=[ info21 ] } InfoStore is { Foo1=[ info11 info12 info13 ] Foo2=[ info21 info22 ] }
内容的提问来源于stack exchange,提问作者Overflow
相关产品推荐
相关产品推荐

