智能指针 auto_ptr、 shared_ptr 、weak_ptr的使用方法及注意事项


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

///////////////////////////////auto_ptr/////////////////////////////////////////////////////////////
//boost中 scoped_ptr和auto_ptr一样 禁止用户进行拷贝和赋值头文件#include<boost/scoped_ptr.hpp>
class A
{
public:
    A(int a ):_a(a)
    {
        cout << "A的构造函数" << endl;

    }
    ~A() 
    {
        cout << "A的析构函数" << endl;

    }
    int _a;
};

int main(void)
{

    //c++ 98的auto_ptr
    auto_ptr<A>p1(new A(100));
    auto_ptr<A>p1(new A[100]);//报错 不能接受数组指针可以使用scoped_arry
    cout << p1->_a << endl;

    auto_ptr<A>p2(p1);//调用拷贝构造或赋值函数函数,此时P1失去所有权
    auto_ptr<A>p2;
    p2 = p1;
    p1->_a;//报错 p1在拷贝或赋值给p2时失去了所有权 
    p2->_a;//正确
    
    return 0;
}


///////////////////////////////shared_ptr 共享所有权的智能指针及shared_ptr使用的四个陷阱/////////////////////////////////////////////////////////////
//shared_ptr使用引用计数加,每个shared_ptr的拷贝都指向相同的内存,每关联拷贝一次 内部引用计数加1
class A
{
public:
    A(int a) :_a(a)
    {
        cout << "A的构造函数" << endl;

    }
    ~A()
    {
        cout << "A的析构函数" << endl;

    }
    int _a;
};

////配合陷阱3循环引用导致的内存泄漏
class B1;
class A1
{
public:
    A1()
    {
        cout << "A1构造函数!" << endl;
    }

    ~A1()
    {
        cout << "A1析构函数!" << endl;
    }
    shared_ptr<B1>m_pb;
};


class B1
{
public:
    B1()
    {
        cout << "B1构造函数!" << endl;
    }

    ~B1()
    {
        cout << "B1析构函数!" << endl;
    }
    shared_ptr<A1>m_pA;
};

int main(void)
{

    //c++11的shared_ptr
    shared_ptr<A>p1(new A(100)); //初始引用计数为1
    cout << "是否是唯一拥有者:" << p1.unique()<<"  " <<"拥有者数量:"<<p1.use_count()<< endl;

    shared_ptr<A>p2(p1);//拷贝引用计数+1为2
    cout << "是否是唯一拥有者:" << p1.unique() << "  " << "拥有者数量:" << p1.use_count() << endl;

    shared_ptr<A>p3;
    p3 = p2;  //赋值拷贝引用计数+1
    cout << "是否是唯一拥有者:" << p1.unique() << "  " << "拥有者数量:" << p1.use_count() << endl;

    cout << "内存:" << p1.get() << "  " << p2.get() <<  "  " << p3.get() << endl;

    //全部释放所有权
    p1.reset(); //引用计数 - 1
    p2.reset(); //引用计数 - 1
    p3.reset(); //引用计数 - 1 这里调用结束A对象析构

    /////////////////////////shared_ptr使用陷阱////////////////////////////////
    //陷阱1.不能使用智能指针指向栈内存地址 所有智能指针皆存在这个陷阱
    int ma = 10;
    shared_ptr<int>p4(&ma);//这里不会出错 但是return后会报错 栈内存释放后 智能指针在释放 释放了两次
    cout << "p4拥有者数量:  " << p4.use_count() << endl;

    //陷阱2.不能多次引用同一原指针,否则同一内存会被delete多次
    int *pInt = new int(10);

    //两个对象管理同一个内存
    shared_ptr<int>p5(pInt);

    //正确的写法 shared_ptr<int>p6(p5);
    shared_ptr<int>p6(pInt); //错误的写法

    //这里输出为1  p2是直接引用原指针 p2无法感知p1
    cout << "p6拥有者数量:"<<p6.use_count()<<endl;

    //陷阱3.循环引用导致的内存泄漏
    shared_ptr<A1>pa(new A1());  //此刻Pa指向的内存引用计数+1
    shared_ptr<B1>pb(new B1());  //此刻Pb指向的内存引用计数+1

    //引用计数+1 都变成了2 在return之后由于m_pa和m_pb是在类中定义的不会自动释放导致引用计数只释放了一次
    //导致两个类析构函数不会执行
    pa->m_pb = pb;//此刻m_pb及m_pb指向的内存引用计数+1=2
    pb->m_pA = pa;//此刻m_pA及pa指向的内存引用计数+1=2

   //陷阱4.shared_ptr管理的内存只能是new出来的不能是malloc出来的

    return 0;
}


////////////////////////////////make_shared/////////////////////////////////////
/*
make_shared的作用:
1、shared_ptr消除了显示的delete调用,很大程度上避免了程序员忘记delete而导致的内存泄漏,
但shared_ptr的构仍按需要new 导致了调用的不对称性,所以用make_shared来构造对象,隐藏new的细节
2、std::make_shared(比起直接使用new)的一个特性是能提升效率。使用std::make_shared允许编译器
产生更小,更快的代码,产生的代码使用更简洁的数据结构。每个std::shared_ptr都指向一个控制块,
控制块包含被指向对象的引用计数以及其他东西。这个控制块的内存是在std::shared_ptr的构造函数中分配的。
因此直接使用new,需要一块内存分配给Widget,还要一块内存分配给控制块。如果使用std::make_shared来替换
一次分配就足够了。这是因为std::make_shared申请一个单独的内存块来同时存放Widget对象和控制块。这个优化
减少了程序的静态大小,因为代码只包含一次内存分配的调用,并且这会加快代码的执行速度,因为内存只分配了一次
*/


class A2
{
public:
    A2(int a, double b):_a(a), _b(b)
    {
        cout << "A1构造函数!" << endl;
    }

    ~A2()
    {
        cout << "A1析构函数!" << endl;
    }
    int _a;
    double _b;

};

int main(void)
{
    int a = 5;
    double b = 1.0;
    //new的方式
    //shared_ptr<A2>p1(new A2(a,b));
    //make_shared的方式
    shared_ptr<A2>p2 = make_shared<A2>(a,b);
    shared_ptr<A2>p3 = p2;
    cout << "拥有者数量:" << p3.use_count()<<endl;
    return 0;
}


////////////////////////////////weak_ptr弱引用/////////////////////////////////////

智能指针 auto_ptr、 shared_ptr 、weak_ptr的使用方法及注意事项


/*
作用:协助shaed_ptr工作 他只可以通过一个shared_ptr或者另一个weak_ptr对象进行构造
他的构造不会引起引用计数的增加或减少,用来监测shared_ptr的拥有者数量 相当于只读不能写
注:weak_ptr监管的是shared_ptr指向的内存,和由谁构造的没关系。
方法: expired 检测所管理的对象是否释放 lock创建新的对shared_ptr并使计数+1  
       reset 将weak_ptr置空 swap交换两个 weak_ptr
运用场景: 解决管理内存是否被释放
解决循环引用问题

在使用weak_ptr是,我们一般会先用expired()判断其是否过期,如果没有过期则可访问所管理的资源
但weak_ptr并不能直接操作资源,想要操作资源的话需要使用lock()函数返回一个shared_ptr来操作资源
*/
class A3
{
public:
    A3(int a, double b):_a(a), _b(b)
    {
        cout << "A1构造函数!" << endl;
    }

    ~A3()
    {
        cout << "A1析构函数!" << endl;
    }
    int _a;
    double _b;

};

int main(void)
{
    int a = 5;
    double b = 1.0;
    shared_ptr<A3>p2 = make_shared<A3>(a,b);
    shared_ptr<A3>p3 = make_shared<A3>(a, b);

    weak_ptr<A3>p4(p2);  //引用计数不变

    cout << "拥有者数量:" << p4.use_count()<<endl;

    //p2.reset()引用计数减1 p2无效 但是对由其构造出来的p4没有影响
    p2.reset();
    cout << p4.expired() << endl;

    shared_ptr<A3>p5 = p4.lock();//lock()返回创建的shared_ptr指针 引用计数+1
    cout << "拥有者数量:" << p5.use_count() << endl;

    cout << "监管的对象是否被释放杆:" << p5._Expired() << endl;

    //释放weak_ptr 但不影响监管的share_ptr对象
    p5.reset();

    return 0;
}

//////////////////weak_ptr解决循环引用的问题/////////////////////////////////////////
class A
{
public:
    A() 
    {
        cout << "A1构造函数!" << endl;
    }

    ~A()
    {
        cout << "A1析构函数!" << endl;
    }
    //shared_ptr<A>m_pa;
    //解决方案 循环引用的时候不会增啊引用计数
    weak_ptr<A>m_pa;
};
int main(void)
{
    shared_ptr<A>Pa(new A());
    //循环引用
    Pa->m_pa = Pa;
    return 0;
}