智能指针类型
在C++程序中,普通变量使用栈内存,为函数运行时专用,结束后会自动释放,无须考虑内存释放问题。 但堆内存是共用的,其使用是通过指针变量的new来分配,使用delete来释放,因指针使用方便,很容易让程序员上瘾,其代价是各种原因造成的内存泄露问题,如:
- 忘记使用delete释放指针变量
- 指针数组,没有释放成员指针的内存
- 指针指向一个新地址,原地址内容未释放
- 异常处理程序,跳过了 delete 语句,致使指针未释放
等等,对服务器应用程序来说,内存泄露常常是致命的。
C++11后,STL增加了智能指针类型,主要有
- std::shared_ptr,
- std::weak_ptr
- std::unique_ptr
智能指针是指针上的包装类,重载了*
和>
等运算符。智能指针类的对象看起来像普通指针。但是,与普通指针不同,在析构函数里释放了指针,因此无须担心忘记释放指针。
unique_ptr用法
unique_ptr是智能指针最简单形式。 unique_ptr指向1个内存对象,而且是指向这个对象的唯一指针,这样可以避免出错。 但尽量不要将unique_ptr做为参数传给第3方接口函数,以避免对方以复制指针的方式来使用它。
使用语法
#include <memory>
using namespace std;
unique_ptr<A> ptr(new A(初始化参数));
// 或者使用make_unique<T>() 模板函数来初始化unique_ptr
unique_ptr<A> ptr = make_unique<A>(初始化参数);
如定义1个int 类型的unique_ptr
int a = 99;
unique_ptr<int> ptr = make_unique<int>(a);
// std::unique_ptr x(new int(99));
// *ptr为指向对象的值,ptr.get()为对象地址
cout << *ptr << ", " << ptr.get() << endl;
手工释放
ptr.reset();
ptr.reset(new int(1)); // 释放后,重新指向新地址
指向数组
unique_ptr<int[]> x(new int[5]);
x[0]=10;
x[1]=11;
用途:代替普通指针,以避免忘记释放
void NotLeaky() {
std::unique_ptr x(new int(5));
... // lots of code
}
unique_ptr指向的对象A,只能被1个指针使用
unique_ptr<A> p1(new A);
p1->printA();
//下面语句会报错
unique_ptr<A> p2 = p1;
p2->printA();
允许复制指针,但复制后原指针被释放
unique_ptr<A> p2 = move(p1);
p2->printA(); // p2已将p1复制
p1->printA(); // 报错,p1 已被释放。
定义1个指向类对象的unique_ptr
class Character {
public:
string Name;
Character(string name = "Frodo") : Name(name)
{
cout << "Greeting: " << Name << endl;
}
~Character() {
cout << "Deleting " << Name << endl;
}
void printName() { cout << "Name: " << Name << endl; }
};
int main() {
unique_ptr<Character> GandalPtr = make_unique<Character>("Gandal");
GandalPtr->printName();
cout << GandalPtr.get() << endl;
return 0;
}
shared_ptr用法
Shared_ptr 指的是,允许多个ptr指向同1块内存,增加了1个reference 计数器
- 当1个新指针指向该内存,reference计数器加1
- 当1个指向从该内存移走后,reference 计数器减1
- 计数器为0时,则释放内存对象。
shared_ptr 适用于给函数传值,类对象引用等各类场景。
创建shared_ptr 指针
std::shared_ptr<int> p1; //不传入任何实参
std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr
std::shared_ptr<int> p3(new int(10));
C++11 标准中还提供了 std::make_shared 模板函数,其可以用于初始化 shared_ptr 智能指针,例如:
// 指向int 数据
std::shared_ptr<int> p3 = std::make_shared<int>(10);
// 指向类对象
shared_ptr<Rectangle> p1(new Rectangle(10, 5));
p1->getArea() //访问成员
//调用拷贝构造函数复制指针
std::shared_ptr<int> p2(p1);
//或者 std::shared_ptr<int> p2 = p1;
用法示例
#include <iostream>
#include <memory>
using namespace std;
int main()
{
//构建 2 个智能指针
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);
//输出 p2 指向的数据
cout << *p2 << endl;
p1.reset();//引用计数减 1,p1为空指针
if (p1) {
cout << "p1 不为空" << endl;
}
else {
cout << "p1 为空" << endl;
}
//以上操作,并不会影响 p2
cout << *p2 << endl;
//判断当前和 p2 同指向的智能指针有多少个
cout << p2.use_count() << endl;
return 0;
}
程序执行结果为:
10
p1 为空
10
1
weak_ptr 指针用法
weak_ptr不能单独使用,只能和 shared_ptr 类型指针搭配使用,区别是其内部无对象引用计数器。
使用方式:
(1) 先创建一个空 weak_ptr 指针,例如:
std::weak_ptr wp1;
将其指向 shared_ptr 指针变量,即与shared_ptr指向同1个内存对象
std::shared_ptr<int> sp (new int);
std::weak_ptr<int> wp3 (sp);
wp3与sp指向相同的内存区,由于 weak_ptr没有重载* 与->操作符,因此不能直接访问对象成员与方法。weak_ptr不会引起对象引用计数器的变化,如下
std::shared_ptr<int> sp1(new int(10));
std::shared_ptr<int> sp2(sp1);
std::weak_ptr<int> wp(sp2);
//输出和 wp 同指向的 shared_ptr 类型指针的数量
cout << wp.use_count() << endl;
如果要访问weak_ptr指向对象的成员或方法,需要借助 lock() 函数,返回一个和 weak_ptr 同指向的 shared_ptr 类型的指针,通过其来访问对象成员。
//访问对象的getArea()方法
cout << wp.lock()->getArea() << endl;
// 或者采用下面的方式,更安全。
auto sp = wp.lock() ;
if (sp) {
sp->getArea();
}
总结
为了避免C++程序产生内存泄露的风险,应该掌握 unique_ptr, shared_ptr, weak_ptr 3种智能指针类型的使用方式与应用场景,通过智能指针的特性来实现内存自动释放与回收。