C++智能指针(一)
c++中的动态内存的管理是通过一对运算符来管理的:new ,在动态内存中为对象分 配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化,delete,接受一个对象的指针,销毁对象,并且释放与之关联的内存。
动态内存的使用很容易造成问题,因为确保在正确的时间释放内存是特别困难的。如果我们初始化指针后未调用delete释放分配的内存,就会造成内存泄露,解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
c++新的标准库提供了两种智能指针类型来动态管理对象。智能指针的行为类似于常规指针,重要的区别就是在于底层管理指针的方式:shared_ptr允许多个指针指向同一个对象。unique_ptr则“独占”所指向的对象。标准库还定义了一个名为weak_ptr的伴随类,是一种弱引用,指向shared_ptr所管理的对象。这三种类型的定义都在头文件memory中。
shared_ptr
和vector类似,智能指针也是模板,因此我们在创建智能指针时必须指明指针可以指向的类型。同vector一样我们在尖括号内给出类型,之后是所定义的智能指针的名字。
//shared_ptr 可以指向int
std::shared_ptr<int> p1;
//shared_ptr 可以指向string
std::shared_ptr<std::string> p2;
初始化
智能指针的使用方法和普通指针类似,解引用一个智能指针返回它指向的对象。默认初始化的智能指针包含一个空指针,如果在条件判断中使用智能指针,其作用就是检查它是否为空。
//如果p2不为空。检查其是否指向一个空string
if (p2 && p2->empty())
{
*p2 = "Hi";
}
最安全的分配和使用动态内存的方法是调用标准库中的一个名为make_shared的函数,此函数在动态内存中分配一个对象并初始化它
// 1 通过构造函数、std::shared_ptr辅助函数、reset方法来初始化
std::shared_ptr<int> p3(new int(1));
//error 缺少类模板的参数列表
//std::shared_ptr p4 = p3;
std::shared_ptr<int> p4 = p3;
std::shared_ptr<int> p5;
// 与赋值类似,reset会更新引用计数,p5指向一个新对象,p5原来指向的对象计数-1
p5.reset(new int(1));
if (p5) {
std::cout << "p5 not null" << std::endl;
}
//应该优先使用make_shared来构造智能指针,更高效
auto p1 = std::make_shared<int>(100);
// 相当于
std::shared_ptr<int> sp1(new int(100));
拷贝、赋值
进行拷贝或者赋值操作时,每个shared_ptr都会记录有多少个其他shared_ptr指向相同的对象;我们可以理解为每个shared_ptr都有一个关联的计数器,通常称为引用计数,无论何时我们拷贝一个shared_ptr时,计数器都会递增。当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时计数器就会递减,当一个shared_ptr的计数器变为0时会自动释放自己所管理的对象。
std::shared_ptr<int> sp1(new int(100));
std::shared_ptr<int> p = sp1;
常用函数
share_ptr 和 unique都支持的操作:
|
|
*p |
解引用p,获得它指向的对象 |
p->mem |
等价于*p->mem |
p.get() |
返回p中保存的指针。要小心使用,若智能指针释放了对象,返回的指针所指向的对象也消失了 |
swap(p,q)/p.swap(q) |
交换p,q中的指针 |
share_ptr独有的操作:
|
|
make_shared(args) |
返回一个shared_ptr,指向一个动态分配类型为T的对象,使用args初始化此对象 |
shared_ptrp(q) |
p是shared_ptr q的拷贝,此操作会递增q中的计数器 |
p = q |
p和q都是shared_ptr,所保存的指针类型必须能互相转换,此操作会递减p的引用计数,递增q的引用计数,如果p的引用计数变为0,则会将其管理的原内存释放 |
p.unique() |
如果p的use_count为1返回true,否则返回false |
p.use_count() |
返回与p共享的智能指针数量 |
unique_ptr
一个unique_ptr拥有它所指向的对象,和shared_ptr不同,某个时刻智只能有一个unique_ptr指向一个定对象,当unique_ptr被销毁时其指向的对象也会被一同销毁。
初始化
和shared_ptr不同,没有类似于make_shared的标准库函数返回一个unique_ptr。必须采用直接初始化的形式。
std::unique_ptr<std::string> p1;
std::unique_ptr<std::string> p2(new std::string("Hi"));
拷贝、赋值
因为unique_ptr拥有其指向的对象,因此unique_ptr不支持普通的拷贝或者赋值操作。
//error
p1 = p2;
//error
std::unique_ptr<std::string> p3(p2);
虽然不能通过拷贝或者赋值一个unique_ptr,但是可以通过调用release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique;
std::unique_ptr<std::string> p1(new std::string("Hi"));
//release成员返回unique当前保存的指针并将其置为空
//将p1指向对象的所有权转移给p2,release将p1置为空
std::unique_ptr<std::string> p2(p1.release());
std::unique_ptr<std::string> p3(new std::string("No!"));
//所有权从p3转移给p2,reset释放了p2原本指向的内存
p2.reset(p3.release());
/*
release会切断unique_ptr和它原本管理的对象之间的联系,release返回的指针一般被用来初始化另一个智能指针或者给另
一个智能指针赋值,如果不用另一个智能指针来保存release返回的指针,我们必须手动释放资源,
*/
//error,p2不会释放内存,并且我们丢失了指针
p2.release();
//yes,但是记得delete(p)
auto p = p2.release();
常用函数
|
|
unique_ptr u1、unique_ptr<T,D> u2 |
空unique_ptr,可以指向内心为T的对象,u1会使用delete来释放他的指针;u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr<T,D> u(d) |
空 unique_ptr,指向类型为T的对象,用类型为D的对象代替delete |
u = nullptr |
释放u指向的对象,将u置为空 |
u.release() |
u放弃对指针的控制权,返回指针并将u置为空 |
u.reset() |
释放u指向的对象 |
u.reset(q)、u.reset(nullptr) |
如果提供了内置指针q,则u指向这个对象,否则将u置为空 |
weak_ptr
weak_ptr是一种不控制对象生存期的智能指针,它指向一个由shared_ptr管理的对象,将weak_ptr绑定到一个由shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,weak_ptr即使指向该对象,这个对象也会被释放(弱引用)。
初始化
创建一个weak_ptr时要用一个shared_ptr来初始化它。
auto p7 = std::make_shared<int>(100);
//p弱共享p7,p7的引用计数不改变
std::weak_ptr<int> p(p7);
由于对象有可能不存在,因此不能使用weak_ptr直接访问对象,而必须调用lock,此函数会检查weak_ptr指向的对象是否存在,如果存在,lock会返回一个指向共享对象的shared_ptr。
if (p7 == p.lock())
{
//在这个if里面 p7和p共享对象
}
常用操作
|
|
weak_ptrw |
空weak_ptr可以指向类型为T的对象 |
weak_ptr w(sp) |
与shared_ptr sp指向相同对象的weak_ptr |
w = p |
p可以是weak_ptr,也可以是weak_ptr,赋值后w与p共享对象 |
w.reset() |
将w置为空 |
w.use_count() |
与w共享对象的shared_ptr的数量 |
w.expired() |
如果w.use_count()为0返回true,否则返回false |
w.lock() |
如果expired为true,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr |
参考书籍 :
《C++ Primer 第五版》