C++修改std::vector存储对象值失效 如何正确存储可修改对象?
问题根源
该问题的核心是lambda捕获的this指针不会随对象拷贝自动更新,和std::vector的存储特性相关,具体逻辑如下:
- 你在
Thing的构造函数里创建的lambda捕获了当前对象的this指针,这个指针指向构造时的原始对象(也就是你代码里的局部变量a) - 调用
mythings.push_back(a)时,会调用Thing的默认拷贝构造函数生成一个新的Thing对象存入vector,默认拷贝构造只会原样复制父类的myfuncs容器,里面lambda保存的this还是原来局部变量a的地址 - 后续你调用
mythings[0].setValue修改的是vector里新对象的value,但lambda执行时访问的是原局部变量a的value,这个值从初始化后就没被修改过,默认值为0,所以输出一直是0
解决方案
有两种常用的方案可以解决这个问题:
方案1:容器存储智能指针,避免对象拷贝
直接让容器存储std::unique_ptr<Thing>或者std::shared_ptr<Thing>,对象只会被构造一次,不会发生拷贝,lambda捕获的this始终指向正确的对象。修改后的main函数代码如下:
int main() { std::vector<std::unique_ptr<Thing>> mythings; // 直接构造对象存入智能指针,不需要额外的局部变量 mythings.push_back(std::make_unique<Thing>()); mythings[0]->setValue(666); mythings[0]->toggle(); mythings[0]->setValue(999); mythings[0]->toggle(); return 0; }
方案2:自定义拷贝逻辑,更新lambda的this指针
如果你确实需要在容器里存储对象本身,就要为Thing类实现自定义的拷贝构造、拷贝赋值函数,拷贝时重新生成绑定了新对象this的lambda,替换掉复制过来的旧lambda:
class Thing: public OtherThing{ public: Thing(){ std::function<int()> myfunc= [this]()->int{ std::cout << "from lambda " << this->value << std::endl; return this->value; }; this->addElement(myfunc); } // 自定义拷贝构造函数 Thing(const Thing& other){ value = other.value; // 重新生成绑定当前新对象this的lambda std::function<int()> myfunc= [this]()->int{ std::cout << "from lambda " << this->value << std::endl; return this->value; }; this->addElement(myfunc); } // 自定义拷贝赋值运算符 Thing& operator=(const Thing& other){ if (this != &other) { value = other.value; // 清空旧的lambda,添加新的绑定当前this的lambda myfuncs.clear(); std::function<int()> myfunc= [this]()->int{ std::cout << "from lambda " << this->value << std::endl; return this->value; }; this->addElement(myfunc); } return *this; } void setValue(int value){ this->value = value; } int getValue(){ return this->value; } private: int value = 0; // 注意要让子类能访问父类的myfuncs,可以把父类的myfuncs改为protected权限,或者在父类提供修改接口 };
一般来说方案1的实现成本更低,也更符合C++的常规使用习惯,推荐优先使用。
内容的提问来源于stack exchange,提问作者Xgo
相关产品推荐
相关产品推荐

