c++ 11中的智能指针使用介绍

c++11 关于auto_ptr

  1. 智能指针需要引用头文件 <memory>
  2. c++ 11不再建议使用auto_ptr,编译时会产生 deprecated(及不赞成) 告警
  3. auto_ptr的功能可以被unique_ptr完全替换

auto_ptr代码示例

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    auto_ptr<string> p1 (new string("point to p1"));
    auto_ptr<string> p2 = p1;  // 此处赋值不会报错,但是会导致p1悬空
    //后续对于p1的引用会崩溃
    cout << "p2: " << *p2 << " ; p1: " << *p1 << endl;
}

unique_ptr代码示例

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    unique_ptr<string> p1(new string("point to p1"));
    //unique_ptr<string> p2 = p1;	// 赋值函数被声明为删除,
    // 不允许外部引用赋值,编译时会报错
    // 用编译期检查的手段 规避了悬空指针再次引用导致的崩溃问题
    
    unique_ptr<string> p2 = move(p1);	// 但是允许入参为 右值引用 的赋值函数
    // 在用户明确知晓该特性,但仍执意赋值的情况下,仍可以使用右值引用的方式进行赋值

    unique_ptr<string>&& p3 = move(p1);
    //p2 = p3;	//但是对于右值引用变量,编译时被作为左值处理,
    			//所以这种赋值方式仍会编译异常

	// 下文使用完美转发语义,可以解决右值引用被作为左值变量处理的问题
    unique_ptr<string> p4 = forward<unique_ptr<string>&&>(p3);	
    
    // 由于p1已经被右值引用赋值给p2,后文的p3和p4本质上都是对于p1的再次引用,
    // 都会是悬空指针,如果尝试输出,会产生崩溃,
    // 但是上述写法不影响对于定义的解释,可以用其他变量代替p1,来规避崩溃的问题
    cout << "p2: " << *p2 << " ; p1: " << endl;
}

为了适配多次引用的情况,智能指针中增加引用计数,引入了shared_ptr类,可以采用make_shared<T>函数,或new运算符实现初始化;建议采用前者
但是还存在一个问题,就是循环引用会导致无法实现自动释放的功能。

#include <iostream>
#include <memory>
using namespace std;

class A { 
public:
    A() { cout << "A cons" << endl; }
    ~A() { cout << "A des" << endl; }
    shared_ptr<A> _ptr;		// 循环引用指针变量
    shared_ptr<A> _ptr2;
    shared_ptr<A> _ptr3;
    shared_ptr<A> _ptr4;
};

int main()
{
    A *pa = new A;
    shared_ptr<A> ptr_a(pa);
    shared_ptr<A> ptr_b(pa);	// 使用new出来的指针初始化,具有重复初始化的风险,
    							// 会造成重复释放pa地址的崩溃问题
    auto ptr1 = make_shared<A>();
    cout << ptr1.use_count() << " " << ptr1.get() << endl;
    ptr1->_ptr = ptr1; 		// 空指针赋值会造成引用计数增加
    ptr1->_ptr = ptr1; 
    ptr1->_ptr = ptr1; 		// 非空指针的重复赋值不会造成计数重复增加,此处会输出2
    cout << ptr1.use_count() << " " << ptr1.get() << endl;
    ptr1->_ptr2 = ptr1;
    ptr1->_ptr3 = ptr1;
    ptr1->_ptr4 = ptr1;		// 多个空指针的赋值会导致引用计数不断增加,此处在2基础上又加3,会输出5
    cout << ptr1.use_count() << " " << ptr1.get() << endl;
    
    // 程序结束时,仅有ptr1被析构,引用计数减1,为4,不为0,所以ptr1指向的A对象的实例不会被释放
    // 因为实例不被释放,所以成员变量_ptrx都不会被析构,
    // 继而引用计数永远不会减小,实例永远不会被自动释放的问题
    return 0;
}

针对该问题,引入了weak_ptr,该类型的变量可以接收shared_ptr类型的指针,且赋值时不会导致其引用计数增加

#include <iostream>
#include <memory>
using namespace std;

class A {
public: 
    A() { cout << "A cons" << endl; } 
    ~A() { cout << "~A desc" << endl; }
    weak_ptr<A> _ptr;
};

int main()
{
    auto ptr1 = make_shared<A>();
    cout << ptr1.use_count() << " " << ptr1.get() << endl;
    ptr1->_ptr = ptr1;
    cout << ptr1.use_count() << " " << ptr1.get() << endl;
    return 0;
}

上一篇:3


下一篇:shared pool