C++11智能指针介绍
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
为什么要使用智能指针
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间,有效避免内存泄漏的问题。
智能指针定义于 memory (非memory.h)中, 命名空间为 std
unique_ptr
unique_ptr,是用于取代c++98的auto_ptr的产物
unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory (非memory.h)中, 命名空间为 std.
- 1.unique_ptr 是禁止复制赋值的,始终保持一个 unique_ptr 管理一个对象。
- 2.unique_ptr 虽然不能赋值,但可以通过 move() 函数转移对象的所有权。一旦被 move() 了,原来的 up1 则不再有效了。
- 3.reset() 可以让 unique_ptr 提前释放指针。
auto_ptr
(C++98的方案,C++11已经抛弃)采用所有权模式。
auto_ptr 适合用来管理生命周期比较短或者不会被远距离传递的动态对象, 最好是局限于某个函数内部或者是某个类的内部.
由于 auto_ptr 基于排他所有权模式:两个指针不能指向同一个资源,复制或赋值都会改变资源的所有权。
#include
#include
class Test
{
public:
void print()
{
std::cout << "Test::Print" << std::endl;
}
};
int main()
{
std::auto_ptr pTest1(new Test);
pTest1->print();
std::auto_ptr pTest2(pTest1); //复制构造函数
pTest2->print();
std::cout << "pTest1 pointer:" << pTest1.get() << std::endl; //auto_ptr类的成员函数get()返回一个原始的指针
std::cout << "pTest2 pointer:" << pTest2.get() << std::endl;
return 0;
}
//打印结果:
Test::Print
Test::Print
pTest1 pointer:00000000
pTest2 pointer:01659548
//经过复制构造之后,pTest1所指向资源的所有权转向了pTest2,而pTest1变成空,二者不能同时共享该资源。
auto_ptr 主要有两大问题:
- 复制和赋值会改变资源的所有权,不符合人的直觉。
- 在 STL 容器中无法使用auto_ptr ,因为容器内的元素必需支持可复制(copy constructable)和可赋值(assignable)。
shared_ptr(C++11中新增的智能指针)
基于引用计数模型。资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。
每次有 shared_ptr 对象指向资源,引用计数器就加1;当有 shared_ptr 对象析构时,计数器减1;当计数器值为0时,被指向的资源将会被释放掉。且该类型的指针可复制和可赋值,即其可用于STL容器中。此外,shared_ptr 指针可与多态类型和不完全类型一起使用。
std::shared_ptr 智能指针共享所指向的资源(所有权),即几个 shared_ptr 可同时拥有一个对象,且共享一个控制块(constrol block),包含指向资源的 shared_ptr对象个数、指向资源的 weak_ptr 对象个数以及删除器(deleter:用户自定义的用于释放资源的函数,可以默认没有)。
一个空的 shared_ptr 对象不拥有任何资源和控制块。另一方面,一个 shared_ptr 初始化为一个NULL 指针和一个控制块,这有别有空的 shared_ptr。当共享的引用计数器为0时,资源释放(delete 操作符释放,或由用户提供的 删除器 释放它)。
#include
#include
class Test
{
public:
void print()
{
std::cout << "Test::Print" << std::endl;
}
};
int main()
{
std::shared_ptr pTest1(new Test);
pTest1->print();
std::shared_ptr pTest2(pTest1); //复制构造函数
pTest2->print();
std::cout << "pTest1 pointer:" << pTest1.get() << std::endl; //shared_ptr类的成员函数get()返回一个原始的指针
std::cout << "pTest2 pointer:" << pTest2.get() << std::endl;
std::cout << "count pTest1:" << pTest1.use_count() << std::endl; //shared_ptr类的成员函数use_count():返回多少个智能指针指向某个对象,主要用于调试。
std::cout << "count pTest2:" << pTest2.use_count() << std::endl;
return 0;
}
//打印结果:
Test::Print
Test::Print
pTest1 pointer:00C29550
pTest2 pointer:00C29550
count pTest1:2
count pTest2:2
//pTest2创建后,pTest1对资源的所有权并没有被剥夺,而是pTest1 和 pTest2 均指向了资源,且此时资源的引用计数为2。当两个shard\_ptr 指针pTest1、pTest2 超过其作用域时,最后一个析构的指针将会致使资源的释放(因为引用计数为0了)。
shared_ptr的使用
待补充。。。
主要缺点:
shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。
无法检测出循环引用,这会造成资源无法释放,从而导致内存泄露。
为了 fix 这个问题,C++11引入了另一个智能指针:weak_ptr
weak_ptr(C++11中新增的智能指针)
weak_ptr工作原理
weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
作用在于协助 shared_ptr 工作,可获得资源的观测权,像旁观者那样观测资源的使用情况。观察者意味着 weak_ptr 只对 shared_ptr 进行引用,而不改变其引用计数,当被观察的 shared_ptr 失效后,相应的 weak_ptr 也相应失效。
weak_ptr更像是shared_ptr的助手:
- 1、他不像其余三种,可以通过构造函数直接分配对象内存;他必须通过shared_ptr来共享内存。
- 2、没有重载opreator*和->操作符,也就意味着即使分配到对象,他也没法使用该对象
- 3、不主动参与引用计数,即share_ptr释放了,那么weak_ptr所存的对象也释放了。
- 4、使用成员函数use_count()可以查看当前引用计数,expired()判断引用计数是否为空。
- 5、lock()函数,返回一个shared_ptr智能指针:也就是让weak_ptr观测shared_ptr智能指针,并且在需要时候通过lock函数返回一个shared_ptr。
weak_ptr使用
待补充。。。