返回信息流前言: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;
}
这是一条镜像帖。来源:北邮人论坛 / cpp / #90301同步于 2016/2/17
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
[娱乐]大家来找茬
nuanyangyang
2016/2/17镜像同步15 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
Foo * 和unique_ptr<Foo>不是一回事?
Foo * 在赋值的时候并没有进行内存拷贝
unique_ptr<Foo> 在赋值的时候有数据的“移动”和关联堆内存管理
ps:我觉得m[id] = foo.get();这句话在编译的时候应该提示点什么
【 在 d1264003247 的大作中提到: 】
: 应该是f(10)调用完内部的局部变量foo没了,所以分配的内存也没了。C++ Primer说最好不要混着用。。
C++ primer这么摩登,还提到unique_ptr了?这可是C++11的新功能哦。
【 在 FromMars 的大作中提到: 】
: Foo * 和unique_ptr<Foo>不是一回事?
不是一回事。当Foo*型的变量被析构时,什么也不会发生;但当unique_ptr<Foo>被析构时,它指向的对象也会被delete。
: Foo * 在赋值的时候并没有进行内存拷贝
嗯。是。
: unique_ptr<Foo> 在赋值的时候有数据的“移动”和关联堆内存管理
嗯。
: ps:我觉得m[id] = foo.get();这句话在编译的时候应该提示点什么
编译不会出错。unique_ptr并不禁止里面的指针被拿出来。从这个角度看,unique_ptr并不是指向对象的唯一指针,但是“对那个对象生命周期负责的唯一指针”。
讲了, C++ Primer(第五版)讲了很多C++11的新功能
【 在 nuanyangyang 的大作中提到: 】
:
: C++ primer这么摩登,还提到unique_ptr了?这可是C++11的新功能哦。
【 在 reverland 的大作中提到: 】
: 越来越觉得cpp里好多好高级的概念。。。
嗯。不是很喜欢这一点。这让C++太过复杂了。我倒是觉得,如果真的喜欢ownership模式、RAII、move语义,用Rust语言更好。不过Rust有另一套问题:限制得太死了。