BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #90301同步于 2016/2/17
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

[娱乐]大家来找茬

nuanyangyang
2016/2/17镜像同步15 回复
前言:C++11的unique_ptr是个好东西。可以维护指向堆上对象的指针,并保证在这个unique_ptr被析构的时候delete那个对象。这个东西把C++传统的RAII内存管理模式推广到了堆里。 传统方法: struct Bar { int x,y,z; Bar(int _x, int _y, int _z): x(_x), y(_y), z(_z) {} }; void foo() { Bar *bar = new Bar(1,2,3); .... delete bar; // 手动删除 return; } 摩登方法: void foo() { auto bar = make_unique<Bar>(1,2,3); // C++14的make_unique // 相当于unique_ptr<Bar> bar = make_unique<Bar>(1,2,3); // 也相当于unique_ptr<Bar> bar = unique_ptr<Bar>(new Bar(1,2,3)); // 也相当于unique_ptr<Bar> bar(new Bar(1,2,3)); // 也相当于Bar *bar_ptr = new Bar(1,2,3); unique_ptr<Bar> bar(bar_ptr); return; // 这时候bar作用域结束,里面的指针指向的对象被自动delete。 } 如果把unique_ptr赋值给另一个unique_ptr,原先的unique_ptr会失效。也就是说,赋值的时候不是“拷贝”,而是“移动”。 unique_ptr<Bar> bar = make_unique<Bar>(1,2,3); unique_ptr<Bar> bar2 = bar; // 移动赋值,不是拷贝赋值。 // 从现在开始bar2里面有指针,bar里面是空的了。 cout << bar->x << endl; // 出错。bar已经被移走。 cout << bar2->x << endl; // 可以。 而且,unique_ptr可以放到别的更大的对象里。只要放进去了,它指向的对象就由那个拥有unique_ptr的对象来管理。妈妈再也不用担心我的内存。 struct Bar { int x,y,z; Bar(int _x, int _y, int _z): x(_x), y(_y), z(_z) {} }; struct Foo { unique_ptr<Bar> bar; }; unique_ptr<Foo> f() { unique_ptr<Bar> bar = make_unique<Bar>(1,2,3); // 创建堆上的Bar对象。 unique_ptr<Foo> foo = make_unique<Foo>(); // 这个Foo也在堆上。 foo->bar = bar; // 转移了所有权。现在foo->bar指向对象,但局部变量bar失效了。 return foo; // 返回foo,同时将所有权转移给调用者。 } void g() { unique_ptr<Foo> foo = f(); // 获得所有权 cout << foo->bar->x << endl; // unique_ptr可以当指针来用。 map<int, unique_ptr<Foo>> m; // 这个m对象在栈上。 m[42] = foo; // 嗯。这样也可以。foo和里面的bar的内存的管理交给m了。局部变量foo已失效。 m[42] = nullptr; // 现在,重新设置了m[42],原来的值丢了,这时,bar和foo就都被delete了。 return; // 到此为止,没有手动使用任何delete,但也没有任何内存泄露。 } 所以问题来了:下面的代码有什么问题? #include <iostream> #include <memory> #include <list> #include <map> using namespace std; struct Bar { int x,y,z; Bar(int _x, int _y, int _z): x(_x), y(_y), z(_z) {} }; list<unique_ptr<Bar>> bars; // 记录所有的bar map<int, Bar*> m; // 把int型的id对应到Bar指针上。 // f: 创建一个Bar,并利用字典m,把id对应到这个bar对象上。 void f(int id) { unique_ptr<Bar> bar = make_unique<Bar>(10, 20, 30); m[id] = bar.get(); // get可以从unique_ptr里拿出原始指针。但不会使unique_ptr失效。 // 也就是说,unique_ptr负责内存管理,但并不保证它是唯一的指针。 return; // 好像在return之前忘了什么事情。 } int main() { f(10); f(20); cout << m[10]->x << endl; // 有时候这里会打印出垃圾。为什么?明明已经设置m[10]了。 cout << m[20]->x << endl; // 这里也会打印出垃圾。是不是内存访问错误了? return 0; }
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
chenxiansf机器人#1 · 2016/2/17
先顶后看
d1264003247机器人#2 · 2016/2/18
应该是f(10)调用完内部的局部变量foo没了,所以分配的内存也没了。C++ Primer说最好不要混着用。。
FromMars机器人#3 · 2016/2/18
Foo * 和unique_ptr<Foo>不是一回事? Foo * 在赋值的时候并没有进行内存拷贝 unique_ptr<Foo> 在赋值的时候有数据的“移动”和关联堆内存管理 ps:我觉得m[id] = foo.get();这句话在编译的时候应该提示点什么
nuanyangyang机器人#4 · 2016/2/18
【 在 d1264003247 的大作中提到: 】 : 应该是f(10)调用完内部的局部变量foo没了,所以分配的内存也没了。C++ Primer说最好不要混着用。。 C++ primer这么摩登,还提到unique_ptr了?这可是C++11的新功能哦。
nuanyangyang机器人#5 · 2016/2/18
【 在 FromMars 的大作中提到: 】 : Foo * 和unique_ptr<Foo>不是一回事? 不是一回事。当Foo*型的变量被析构时,什么也不会发生;但当unique_ptr<Foo>被析构时,它指向的对象也会被delete。 : Foo * 在赋值的时候并没有进行内存拷贝 嗯。是。 : unique_ptr<Foo> 在赋值的时候有数据的“移动”和关联堆内存管理 嗯。 : ps:我觉得m[id] = foo.get();这句话在编译的时候应该提示点什么 编译不会出错。unique_ptr并不禁止里面的指针被拿出来。从这个角度看,unique_ptr并不是指向对象的唯一指针,但是“对那个对象生命周期负责的唯一指针”。
d1264003247机器人#6 · 2016/2/18
讲了, C++ Primer(第五版)讲了很多C++11的新功能 【 在 nuanyangyang 的大作中提到: 】 : : C++ primer这么摩登,还提到unique_ptr了?这可是C++11的新功能哦。
nuanyangyang机器人#7 · 2016/2/18
【 在 d1264003247 的大作中提到: 】 : 讲了, C++ Primer(第五版)讲了很多C++11的新功能 原来如此
reverland机器人#8 · 2016/2/18
越来越觉得cpp里好多好高级的概念。。。
nuanyangyang机器人#9 · 2016/2/18
【 在 reverland 的大作中提到: 】 : 越来越觉得cpp里好多好高级的概念。。。 嗯。不是很喜欢这一点。这让C++太过复杂了。我倒是觉得,如果真的喜欢ownership模式、RAII、move语义,用Rust语言更好。不过Rust有另一套问题:限制得太死了。