智能指针
C++11中的智能指针主要分为3种:std::unique_ptr、std::shared_ptr,std::weak_ptr,存在于头文件 <memory>中。
对于智能指针的创建一般优先建议,分别使用 make_unique 和 make_shared,而不是使用new的指针来构建。这种方式创建智能指针有如下几个优点:
- 效率高,避免多次的内存分配。智能指针一般包括:指向资源的指针和指向引用计数控制块的指针,使用make的方式可以一次性的分配所有需要的内存。使用
new时分配一次指向资源的内存,使用make的时候,又需要分配一次指向引用计数控制块的内存。 - 提高了异常安全性,避免出现异常时可能导致的内存泄漏。
一、专有指针:std::unique_ptr
std::unique_ptr用于独占资源,不允许存在其它的指针也指向相同的资源。因此std::unique_ptr将不能进行拷贝和赋值,只能进行移动操作。移动std::unique_ptr后源指针将为null,所有的资源将会转移到目的指针上。
1、声明构建专有指针的方法如下:
// 声明空指针
unique_ptr<string> up1;
unique_ptr<string> up2(nullptr);
// 推荐写法直接使用 make_unique 来创建智能指针
unique_ptr<string> up0 = make_unique<string>("test");
// 会代码额外开销,进行了两次内存分配操作
unique_ptr<string> up3(new string("test"));
// 不推荐写法
string* pStr = new string("test");
unique_ptr<string> up4(pStr);
// 专有指针不能赋值,只能转移给另一个专有指针
up1 = std::move(up0);
//up2 = up3; // 错误,无法进行赋值操作
2、专有指针作为函数的形参,只能为引用的形式
由于专有指针独占资源,因此其作为形参时如果以值传递的形式出现,将会导致拷贝了另外的指针来指向相同的资源,因此只能以引用的形式存在。如下所示:
// uPtr为专有指针,只能以引用的形式存在
void tmp_fun1(unique_ptr<SmartPoint> &uPtr)
{
string city_u = uPtr->GetCity();
}
二、共享指针:std::shared_ptr
对于共享资源将使用std::shared_ptr,由多个 std::shared_ptr实例来共同管理对应的资源,通过引用计数来控制何时将释放该资源,当引用计数为0时,该资源将会被释放。
std::shared_ptr内部包含两个指针:
- 指向内存对象的指针:相当于原始指针,指向分配的内存对象
- 指向控制块的指针:该指针相对于原始指针,为多出的部分,其指向控制块。控制块中包含引用计数、弱引用计数,以及其它内容。所有
std::shared_ptr将指向同一个对象,并共用控制块。
1、该指针的构建方式如下所示:
/****** SmartPointer 为自定义类 ******/
// 声明指针
shared_ptr<SmartPointer> sPtr1;
shared_ptr<SmartPointer> sPtr2(nullptr);
// 推荐定义智能指针的方式
shared_ptr<SmartPointer> sPtr3 = make_shared<SmartPointer>(6, "sz02", 88.66);
// 该方式创建指针性能差于make的方式
shared_ptr<SmartPointer> sPtr4(new SmartPointer(6, "sz03", 88.66));
// 不推荐该方法:使用原始指针来构建智能指针
SmartPointer* p1 = new SmartPointer(1, "city01", 10.66);
shared_ptr<SmartPointer> sPtr5(p1);
2、避免给std::shared_ptr的构造函数传递原始指针
SmartPointer* p1 = new SmartPointer(1, "city01", 10.66);
shared_ptr<SmartPointer> sPtr1(p1);
shared_ptr<SmartPointer> sPtr2(p1);
以上代码中使用原始指针 p1来作为参数来分别构建智能指针 sPtr1和sPtr2,此时sPtr1和sPtr2将分别创建对应的控制块,而且各自的控制块中引用计数将为1。当sPtr1的计数为0时,将释放其指向的内存资源。此时sPtr2的计数在为0时,又将释放一次指向的资源,但是该资源已经被前面释放了,因此会导致 doble free的情况。
对于必须使用 new 的原始指针来构建智能指针的情况,建议直接在智能指针的构造函数中使用 new 。
3、引用计数
std::shared_ptr的引用计数是根据当前有多少指针指向对应的资源来计算的
// 构建两个智能指针,其指向整型数值10
shared_ptr<int> sp1(make_shared<int>(10)); // sp1 指向的控制块中引用计数为1
auto sp2(make_shared<int>(10)); // sp2 指向的控制块中引用计数为1
// 创建指针sp3, 让其也指向sp1的资源
shared_ptr<int> sp3(sp1); // sp1 指向的控制块中引用计数将加1,变成2;sp1和sp3共用一个控制块
三、弱指针:std::weak_ptr
当std::shared_ptr可能悬空时使用std::weak_ptr,此时指针所指的对象可能已经被释放了。
std::weak_ptr一般需要使用std::shared_ptr来进行初始化,它不是一个独立的智能指针,也不能解引用,它是std::shared_ptr的增强。它与std::shared_ptr所指的对象相同,但是std::weak_ptr不会增加std::shared_ptr的引用计数。
auto sp1(make_shared<int>(66));
weak_ptr<int> wPtr1(sp1);
浙公网安备 33010602011771号