C++11STL中新增了智能指针,可以有效的防止内存泄露的问题,以下代码除特殊说明均在C++11下编译。
头文件
#include <memory>
shared_ptr 共享指针
共享指针可以方便的在不同函数中传递,并且在所有指针实例被销毁时自动释放空间。
shared_ptr通过引用计数来管理引用状态,每次有新的引用时计数加1,有引用销毁是计数减1,直到计数为0时自动释放内存空间。
只要将new
运算符返回的指针p
交给一个shared_ptr
对象“托管”,就不必担心在哪里写delete p
语句——实际上根本不需要编写这条语句,托管p
的 shared_ptr
对象在消亡时会自动执行delete p
。
妈妈再也不用担心我忘记delete啦
通过shared_ptr
的构造函数,传入让shared_ptr
托管的指针。
例如:
shared_ptr<int> p(new int);
在函数见传递shared_ptr
,示例代码:
#include <memory>
#include <cstdio>
using namespace std;
struct node {
int a, b, c;
char e;
};
int foo(shared_ptr<node> a) {
// p2被传入foo,a指向p2,引用计数加1,现在为3
return a->a + a->b;
// foo函数结束,a被释放,引用计数减1,现在为2
}
// 听说从main函数开始阅读程序是一个好习惯
int main() {
shared_ptr<int> p(new node); // p引用node对象的实例,引用计数现在为1
shared_ptr<int> p2 = p; // p2引用p,引用计数加1,现在为2
foo(p2);
return 0;
// main函数结束,p,p2被释放,引用计数减2,现在为0
// 引用计数为0,自动释放node的实例。
}
shared_ptr
可以通过get
成员函数获取普通指针:
shared_ptr<int> p(new node);
printf("%p
", p.get()); // 输出p指向的实例的地址
shared_ptr
重载了->
和*
运算符,可以直接访问目标对象:
shared_ptr<int> p(new node);
p->a = 5;
printf("%d
", p->a);
printf("%d
", (*p).a);
在C++17中从重载[]
运算符,用于访问数组:
shared_ptr<int[]> p(new int[105]);
p[1] = 1;
p[2] = 5;
for (int i = 3; i <= 100; ++i)
p[i] = i;
int sum = 0;
for (int i = 3; i <= 100; ++i)
sum += p[i];
什么,你想在C++11中用[]
运算符?那么,有请下一位出场。
unique_ptr 单引用指针
unique_ptr一样会自动释放内存,但是只能用一个unique_ptr
指向目标,其他的都会失效。
和Rust
中的所有权机制一样。
在函数见传递unique_ptr
,示例代码:
#include <memory>
#include <cstdio>
using namespace std;
struct node {
int a, b, c;
char e;
};
unique_ptr<node> foo(unique_ptr<node> a) {
// p2被传入foo,a获取p2的所有权,p2失效。
a->a += a->b;
return a;
// foo函数结束,但是a被作为返回值返回,a拥有所有权。
}
void bar(unique_ptr<node> &a) {
// p2被传入foo,a是p2的引用,p2拥有所有权。
a->a += a->c;
return;
// foo函数结束,a被销毁,p2拥有所有权。
}
// 听说从main函数开始阅读程序是一个好习惯
int main() {
unique_ptr<int> p(new node); // p引用node对象的实例。
unique_ptr<int> p2 = p; // p2获取p的所有权,现在p失效。
printf("%p
", p.get()); // p已经失效,输出0x00000000。
printf("%p
", p1.get()); // 输出p2指向的地址。
// 下面是两种函数传递unique_ptr的方式
p2 = foo(p2); // foo函数返回a,p2重新取回所有权。
bar(p2);
return 0;
// main函数结束,p2被销毁,内存自动释放。
}
unique_ptr
可以通过get
成员函数获取普通指针:
unique_ptr<int> p(new node);
printf("%p
", p.get()); // 输出p指向的实例的地址
unique_ptr
重载了->
和*
运算符,可以直接访问目标对象:
unique_ptr<int> p(new node);
p->a = 5;
printf("%d
", p->a);
printf("%d
", (*p).a);
unique_ptr
从重载[]
运算符,用于访问数组:
shared_ptr<int[]> p(new int[105]);
p[1] = 1;
p[2] = 5;
for (int i = 3; i <= 100; ++i)
p[i] = i;
int sum = 0;
for (int i = 3; i <= 100; ++i)
sum += p[i];
weak_ptr 反向指针
不知道大家在之前看shared_ptr
时是否发现,如果有两个对象相互引用,他们的引用计数永远不会归0,他们永远不会被释放,产生循环引用。
------------------- -------------------
| class A | | class B |
|-----------------| |-----------------|
|int x; | use each other |int y; |
|shared_ptr<B> b<--------------------->shared_ptr<A> a |
|-----------------| |-----------------|
这时就需要weak_ptr
出场了:
------------------- -------------------
| class A | | class B |
|-----------------| |-----------------|
|int x; |one direction use |int y; |
|shared_ptr<B> b---------------------->weak_ptr<A> a |
|-----------------| |-----------------|
这是因为weak_ptr
的引用不会被shared_ptr
计算。