Ped*_*o A 1 c++ pointers memory-management reference
我有一个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);
}
};
Run Code Online (Sandbox Code Playgroud)
我从一个简单的开始std::list<Thing>,然后在插入和检索时复制了一切,我不想要这个,因为如果我得到一个副本,改变它不再反映在原始对象上.在寻找解决方案时,我发现了std::reference_wrapper 这个问题,但现在我有另一个问题.
现在使用它们的代码:
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";
}
Run Code Online (Sandbox Code Playgroud)
我main()只是打电话run().我得到的输出是:
50
10101
Not found!!
Run Code Online (Sandbox Code Playgroud)
虽然我在寻找:
50
10101
50
Run Code Online (Sandbox Code Playgroud)
t4当函数返回时,看起来本地声明的对象不再存在,这是有道理的.我可以通过动态分配它来防止这种情况new,但是后来我不想手动管理内存......
如何在不删除temp()功能的情况下修复代码,而无需手动管理内存?
如果我只是使用std::list<Thing>一些建议,肯定会出现问题t4并且temp将不复存在,但会出现另一个问题:例如,代码将不再打印10101.如果我一直在复制东西,我将无法改变存储对象的状态.
谁是存储中的东西?
你的实际问题是所有权.目前,您Storage并不真正包含Things但是它留给Storage管理您放入其中的对象的生命周期的用户.这与std容器的哲学非常相反.所有标准C++容器都拥有放在其中的对象,容器管理它们的生命周期(例如,您只需调用v.resize(v.size()-2)一个向量,最后两个元素就会被销毁).
为何参考?
您已经找到了一种方法来使容器不拥有实际对象(通过使用a reference_wrapper),但没有理由这样做.在一个名为StorageI 的类中,我希望它能保存对象,而不仅仅是引用.而且,这为许多令人讨厌的问题打开了大门,包括未定义的行为.例如这里:
void temp(Storage& storage) {
storage.findById(2).add(1);
Thing t4; t4.add(50);
storage.add(t4);
std::cout << storage.findById(4).getValue() << "\n";
}
Run Code Online (Sandbox Code Playgroud)
你一个参考存储t4在storage.事情是:t4s生命只在该功能结束之前,你最终会有一个悬空参考.你可以存储这样的引用,但它不是很有用,因为你基本上不允许对它做任何事情.
引用不是很酷吗?
目前你可以推送t1,修改它,然后观察对thingy的更改Storage,如果你想模仿Java,这可能没什么问题,但是在c ++中我们习惯于在你推送东西时制作副本的容器(也有方法到创建元素到位,以防你担心一些无用的临时工).是的,当然,如果你真的想要你可以制作一个标准的容器也拿着参考,但让我们绕道而行......
谁收集了所有垃圾?
也许有必要考虑Java是垃圾收集而C++有析构函数.在Java中,您习惯于引用浮动,直到垃圾收集器启动.在C++中,您必须非常了解对象的生命周期.这可能听起来很糟糕,但实际上对于完全控制对象的生命周期非常有用.
垃圾?什么垃圾?
在现代C++中你不应该担心忘记a delete,而是要欣赏拥有RAII的优势.获取初始化资源并了解析构函数何时被调用允许基本上任何类型的资源获得自动资源管理,这是垃圾收集器只能梦想的(想想文件,数据库连接等).
"如何在不删除temp()函数的情况下修复代码,而无需手动管理内存?"
帮助我很多的一个技巧是:每当我发现自己需要手动管理资源时,我会停下来问"别人不能做脏东西吗?".真的非常罕见,我找不到一个标准容器,它完全符合我的需要.在你的情况下,只是让std::list"脏"工作.
如果没有模板,不能是C++,对吗?
我实际上建议你制作Storage一个模板,沿着以下方向:
template <typename T>
class Storage {
private:
std::list<T> list;
//....
Run Code Online (Sandbox Code Playgroud)
然后
Storage<Thing> thing_storage;
Storage<int> int_storage;
Run Code Online (Sandbox Code Playgroud)
是Storage含S- ThingS和intS,分别.通过这种方式,如果您想要使用引用或指针进行实验,您仍然可以实例化Storage<reference_wrapper<int>>.
我错过了什么吗?......可能是参考?
我将无法改变存储对象的状态
鉴于容器拥有该对象,您宁愿让用户对容器中的对象进行引用.例如,使用矢量
auto t = std::vector<int>(10,0); // 10 element initialized to 0
auto& first_element = t[0]; // reference to first element
first_element = 5; // first_element is an alias for t[0]
std::cout << t[0]; // i dont want to spoil the fun part
Run Code Online (Sandbox Code Playgroud)
为了让Storage你的工作与你一起,你只需要findById返回一个参考.作为演示:
struct foo {
private:
int data;
public:
int& get_ref() { return data;}
const int& get_ref() const { return data;}
};
auto x = foo();
x.get_ref = 12;
Run Code Online (Sandbox Code Playgroud)
TL; DR
如何避免手动资源管理?让别人为你做,并称之为自动资源管理:P