C++系列 知识点突破之3种智能指针

  • Post author:
  • Post category:其他



智能指针

的出现是为了解决指针使用场景中的不便。

使用场景及问题:

① 资源释放了但是指针没有置空(野指针、指针悬挂、踩内存)

② 没有释放资源,产生内存泄露

③ 重复释放资源,引发coredump

【野指针:当前指针所指的资源释放后,当前指针没有置空(nullptr)】

【指针悬挂:当前指针所指的资源被另一个指针(这个指针在资源是释放后置空)释放】

【踩内存:当前指针所指的内存资源被释放后没有置空,该内存又被其他资源占用。野指针和悬挂指针都可能踩内存】

解决思想:

RAII

,利用对象的生命周期对程序资源进行控制。有不同种类的智能指针:



shared_ptr

:记录有多少个shared_ptrs共同指向一个对象,即引用计数。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。



weak_ptr

:shared_ptr解决了很大一部分问题,同时带来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptr和weak_ptr一起引入了标准库,用来解决循环引用的问题。可以理解为weak_ptr就是shared_ptr的辅助。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。



unique_ptr

:定义在<memory>中的智能指针。它持有对对象的独有权,即两个unique_ptr不能指向一个对象,

不能进行复制操作只能进行移动操作

。它被设计成为一个

零额外开销

的智能指针,使用它相比手工写new和delete没有额外开销,不管是时间还是空间上。对unique_ptr来讲,在使用静态删除器(默认作为一个静态信息存放在模板的参数里)时,它的动态信息,每对象的空间开销,则只有一个指针的大小。

【了解环状引用】

class ClassA
{
public:
 ~ClassA() { cout << "A Destructor" << endl; }
 shared_ptr<ClassB> pb; // 在A中通过shared_ptr引用B
};
 
class ClassB
{
public:
 ~ClassB() { cout << "B Destructor" << endl; }
 shared_ptr<ClassA> pa; // 在B中通过shared_ptr引用A
};
 
int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
}


① 程序中创建了一个类A的对象和一个类B的对象并通过shared_ptr指向它们,之后通过指向另一个对象的智能指针对自身的数据成员(pa或pb)进行赋值,因此两个对象的引用计数都为2.

② 当程序退出时,main函数中创建的智能指针(spa和spb)由于生命周期结束,其所指向对象的引用计数减1,但是由于环状引用,对象内部的智能指针(pa和pb)的生命周期都不会结束,两个对象(对象A和对象B)的引用计数始终为1。具体来讲,就是对象A中的智能指针pb只有在对象A析构之后才会结束其生命周期,从而将对象B的引用计数降为0;但是对象A并不会被销毁,因为对象A销毁的条件是指向A的智能指针的引用计数降为0,而对象B指向A的智能指针只有在B对象析构后才会结束其生命周期,从而将对象A的引用计数降为0。因此上述逻辑就成了一个死结,就好像两个人大家都就揪着对方的头发,并且叫嚣着只要对方松手自己就松手,但是两个人谁也不愿意首先让步,结果这两个人始终保持着同样的姿势。

class ClassA
{
public:
 ~ClassA() { cout << "A Destructor" << endl; }
 weak_ptr<ClassB> pb; // 在A中通过weak_ptr引用B
};
 
class ClassB
{
public:
 ~ClassB() { cout << "B Destructor" << endl; }
 weak_ptr<ClassA> pa; // 在B中通过weak_ptr引用A
};
 
int main() {
 shared_ptr<ClassA> spa = make_shared<ClassA>();
 shared_ptr<ClassB> spb = make_shared<ClassB>();
 spa->pb = spb;
 spb->pa = spa;
}


将类中的智能指针类型有shared_ptr改为weak_ptr

,在main函数中初始化两个智能指针时会将对象的引用计数加1,但是对类中的数据成员进行赋值时不会增加对象的引用计数。程序退出时,main函数中创建的两个智能指针生命周期结束,对象的引用计数由1减为0,对象空间释放。

【了解unique_ptr

(点此处学习详细内容)

unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

①unique_ptr中的源代码(MSVC),拷贝构造函数和拷贝赋值运算符均为delete;

unique_ptr<Widget> w1(new Widget(1, 2, 3));
w1->print();
//auto w2 = w1; 错误,编译报错
auto w2 = unique_ptr<Widget>(new Widget(10, 20, 30));//移动操作,等号左侧为右值
auto w2 = std::move(w1); //移动操作  //w1 变成 nullptr
w2->print();

② w1将内存所有权转交为w2,测试打印出来w1会是空指针;

std::unique_ptr<std::string> foo()
{
    return std::make_unique<std::string>("foo");
}

int main()
{
    std::shared_ptr<std::string> sp1 = foo();

    auto up = std::make_unique<std::string>("Hello World");
    std::shared_ptr<std::string> sp2 = std::move(up);
    //std::shared_ptr<std::string> sp3 = up; 错误,编译报错
}

③ 可以转换成shared_ptr;



版权声明:本文为qq_41343328原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。