【发布时间】:2018-06-01 18:26:24
【问题描述】:
我有一个 Storage 类,其中包含 Things 的列表:
#include <iostream>
#include <list>
#include <functional>
class Thing {
private:
int id;
int value = 0;
static int nextId;
public:
Thing() { this->id = Thing::nextId++; };
int getId() const { return this->id; };
int getValue() const { return this->value; };
void add(int n) { this->value += n; };
};
int Thing::nextId = 1;
class Storage {
private:
std::list<std::reference_wrapper<Thing>> list;
public:
void add(Thing& thing) {
this->list.push_back(thing);
}
Thing& findById(int id) const {
for (std::list<std::reference_wrapper<Thing>>::const_iterator it = this->list.begin(); it != this->list.end(); ++it) {
if (it->get().getId() == id) return *it;
}
std::cout << "Not found!!\n";
exit(1);
}
};
我从一个简单的std::list<Thing> 开始,但随后在插入和检索时会复制所有内容,我不希望这样,因为如果我得到一个副本,更改它不会再反映在原始对象上。在寻找解决方案时,我发现了 std::reference_wrapper on this SO question,但现在我遇到了另一个问题。
现在是使用它们的代码:
void temp(Storage& storage) {
storage.findById(2).add(1);
Thing t4; t4.add(50);
storage.add(t4);
std::cout << storage.findById(4).getValue() << "\n";
}
void run() {
Thing t1; t1.add(10);
Thing t2; t2.add(100);
Thing t3; t3.add(1000);
Storage storage;
storage.add(t3);
storage.add(t1);
storage.add(t2);
temp(storage);
t2.add(10000);
std::cout << storage.findById(2).getValue() << "\n";
std::cout << storage.findById(4).getValue() << "\n";
}
我的main() 只是调用run()。我得到的输出是:
50
10101
Not found!!
虽然我一直在寻找:
50
10101
50
问题
看起来当函数返回时,本地声明的对象t4 不再存在,这是有道理的。我可以通过动态分配它来防止这种情况,使用new,但是我不想手动管理内存......
如何在不删除temp() 函数且无需手动管理内存的情况下修复代码?
如果我只是按照一些建议使用std::list<Thing>,那么t4 和temp 的问题肯定会不复存在,但会出现另一个问题:例如,代码将不再打印10101 .如果我继续复制东西,我将无法更改存储对象的状态。
【问题讨论】:
-
@Hamsterrific 不要使用
new创建新对象。现代代码使用std::make_unique或std::make_shared。见stackoverflow.com/questions/106508/… -
“所以唯一的解决方案就是在这里真正使用指针”不是真的。只需创建一个
std::list<Thing>并让列表拥有Things,问题就消失了 -
不要混淆指针和动态分配。考虑所有权。谁负责照顾和喂养并最终释放物体。
-
@Hamsterrific 惯用的解决方案是将
Thing存储在Storage中,然后获取并使用对该元素的引用,因为Storage确实拥有Things。按照现在的设计,Storage似乎实际上共享所有权。Storage必然需要延长Things 的生命周期,直到它自己的生命周期结束,但您似乎还希望run函数将其保持在本地。在这种情况下,std::list<std::shared_ptr<Thing>>将满足您的目的。std::shared_ptr可以被复制和分配,最后一个副本将在Thing被销毁时清理它。 -
由于未定义行为的概念,C++ 可能是通过反复试验来学习的最糟糕的语言。有时你会做一些非常糟糕的事情,但程序“工作”——即产生预期的结果——但一旦你添加了一段完全不相关的代码,它就会神奇地停止工作。给自己买一本好书,系统地学习。
标签: c++ pointers memory-management reference