探索:智能指针(c++)

一、智能指针的概念

C++中堆内存对象需要手动使用new来创建,如果忘记delete操作则会造成内存泄漏的问题。诸如Java、C#等语言则直接提供垃圾回收机制来处理不使用的对象,因此C++在ISO 98标准中也引入了智能指针的概念,并在C++11中趋于完善。

使用智能指针,可以让堆内存的对象不使用delete就可以在特定的时机销毁。

智能指针对象位于栈内存,管理一个堆内存的对象,当智能指针对象的生命周期结束后,会在析构函数中释放掉管理的堆内存资源,从而防止堆内存对象内存泄漏。

C++中有四种智能指针:auto_ptr(自动指针)、unique_ptr(唯一指针)、shared_ptr(共享指针)与weak_ptr(虚指针),其中后三者是C++11中引入的,auto_ptr在C++11以后已经被弃用。

使用智能指针需要引入头文件 #include 

二、auto_ptr

基础使用如下。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    {
        Test* t = new Test("A"); 
        auto_ptr<Test> ap1(t); // 智能指针
        // 从智能指针对象中取出被管理的资源对象
        Test* t2 = ap1.get();
        t2->show();
//        delete t2; 错误:既然已经放手了,就别再插手
        // 解除ap1对资源的管理,ap1仅仅放弃管理权,不会销毁资源
        ap1.release();
        delete t;

        //ap2持有资源B
        auto_ptr<Test> ap2(new Test("B"));
        // 解除ap2对资源的管理并销毁资源对象
        ap2.reset();

        auto_ptr<Test> ap3(new Test("C"));
        // ap3持有的资源从C更换为D,C销毁
        ap3.reset(new Test("D"));
        ap3.get()->show();
    }

    cout << "主函数执行结束" << endl;
    return 0;
}

 

auto_ptr容易让人产生误用的地方是其复制语义,当执行复制相关操作时(拷贝构造函数、赋值运算符),会出现资源控制权转移的问题

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    {
        auto_ptr<Test> ap1(new Test("A"));
        // 显式调用拷贝构造函数
        auto_ptr<Test> ap2(ap1);
        // 隐式调用拷贝构造函数
        auto_ptr<Test> ap3 = ap2;
        auto_ptr<Test> ap4;
        ap4 = ap3; // 赋值运算符
        // 指针转移
        cout << ap1.get() << endl; // 0
        cout << ap2.get() << endl; // 0
        cout << ap3.get() << endl; // 0
        cout << ap4.get() << endl; // 0x7a0fa8

        // 忘记转移了。。。
//        ap1.get()->show(); 错误
    }

    cout << "主函数执行结束" << endl;
    return 0;
}

三、unique_ptr

unique_ptr作为对auto_ptr的改进,其对象对资源具有唯一的控制权,无法通过常规的复制语义进行控制权的转移。

可以通过move函数来完成控制权的转移。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    {
        unique_ptr<Test> up1(new Test("A"));
        // 显式调用拷贝构造函数
        //        unique_ptr<Test> up2(up1); 错误
        // 隐式调用拷贝构造函数
        //        unique_ptr<Test> up3 = up2; 错误
        unique_ptr<Test> up4;
        // 赋值运算符
        //        up4 = up3; // 错误

        // 之前的复制语义结合move函数仍然可以转移控制权
        unique_ptr<Test> up2(move(up1));
        unique_ptr<Test> up3 = move(up2);
        up4 = move(up3);

        cout << up1.get() << endl; // 0
        cout << up2.get() << endl; // 0
        cout << up3.get() << endl; // 0
        cout << up4.get() << endl; // 0xc50fa8

        unique_ptr<Test> up5(new Test("B"));
        up4.swap(up5); // 交换控制权
        up4.get()->show(); // B成员函数
        up5.get()->show(); // A成员函数
    }

    cout << "主函数执行结束" << endl;
    return 0;
}

四、shared_ptr

unique_ptr对持有的资源具有独占性,而shared_ptr则可以把持有的资源在多个shared_ptr对象之间共享。

shared_ptr有两种初始化的方式:

" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    {
       // 使用new来初始化
       shared_ptr<Test> sp1(new Test("A"));
       // 使用make_shared函数来初始化
       shared_ptr<Test> sp2 = make_shared<Test>("B");

       sp1.get()->show();
       sp2.get()->show();
    }

    cout << "主函数执行结束" << endl;
    return 0;
}

make_shared函数相比传统new的方式区别如下:

1. 性能略高

2. 安全性更好

3. 内存释放可能延迟

推荐使用make_shared来创建shared_ptr对象。

每多一个shared_ptr持有同一资源,每个share_ptr内部的引用计数将增加1,每一个持有该资源的shared_ptr销毁时,引用计数将减1,直到引用计数减为0,则销毁持有的资源。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    shared_ptr<Test> sp4;
    {
        cout << "{" << endl;
        shared_ptr<Test> sp1= make_shared<Test>("A");
        cout << "引用计数:" << sp1.use_count() << endl; // 1
        shared_ptr<Test> sp2(sp1);
        shared_ptr<Test> sp3 = sp2;
        sp4 = sp3;
        cout << "引用计数:" << sp1.use_count() << endl; // 4
        cout << "引用计数:" << sp2.use_count() << endl; // 4
        cout << "引用计数:" << sp3.use_count() << endl; // 4
        cout << "引用计数:" << sp4.use_count() << endl; // 4
        cout << "}" << endl;
    }
    cout << "引用计数:" << sp4.use_count() << endl; // 1

    cout << "主函数执行结束" << endl;
    return 0;
}

再补充几个操作函数:

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    shared_ptr<Test> sp4;
    cout << sp4.operator bool() << endl; // false
    cout << sp4.get() << endl; // 0
    {
        cout << "{"  << endl;
        shared_ptr<Test> sp1= make_shared<Test>("A");
        // 判断是否是独自持有资源
        cout << sp1.unique() << endl; // 1

        shared_ptr<Test> sp2(sp1);
        cout << sp1.unique() << endl; // 0

        sp4 = sp2;

        // 获得资源对象的引用
        Test &t = sp2.operator *();
        t.show();

        // 如果持有资源,则返回true
        cout << sp2.operator bool() << endl;

        cout << "}"  << endl;
    }

    cout << "主函数执行结束" << endl;
    return 0;
}

五、weak_ptr

weak_ptr无法控制资源,只能协助shared_ptr进行工作。因为不控制资源,因此weak_ptr不会影响引用计数。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    shared_ptr<Test> sp1;
    {
        cout << "{" << endl;
        shared_ptr<Test> sp2 = make_shared<Test>("A");
        sp1 = sp2;
        // 创建一个weak_ptr对象
        weak_ptr<Test> wp1(sp2);
        weak_ptr<Test> wp2 = wp1;
        weak_ptr<Test> wp3;
        wp3 = sp1;

//        wp3.get(); 错误:无法直接获得资源

        cout << wp1.use_count() << endl; // 2
        cout << wp2.use_count() << endl; // 2
        cout << wp3.use_count() << endl; // 2
        cout << sp1.use_count() << endl; // 2
        cout << sp2.use_count() << endl; // 2
        cout << "}" << endl;
    }
    cout << sp1.use_count() << endl; // 1

    cout << "主函数执行结束" << endl;
    return 0;
}

可以从weak_ptr中获得shared_ptr对象,从而持有资源。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    shared_ptr<Test> sp1 = make_shared<Test>("A");
    weak_ptr<Test> wp1(sp1);
    {
        cout << "{" << endl;
        cout << wp1.use_count() << endl; // 1
        // 通过wp1来获得shared_ptr对象
        shared_ptr<Test> sp2 = wp1.lock();
        sp2.get()->show();
        cout << wp1.use_count() << endl; // 2

        cout << "}" << endl;
    }
    cout << sp1.use_count() << endl; // 1

    cout << "主函数执行结束" << endl;
    return 0;
}

需要注意的是,weak_ptr对资源的持有可能失效,此时lock也会失效。因此,在执行lock函数之前,要先检测资源是否已经失效。

#include <iostream>
#include <memory>

using namespace std;

class Test
{
private:
    string name;

public:
    Test(string name):name(name)
    {
        cout << name << "构造函数" << endl;
    }

    void show()
    {
        cout << name << "成员函数" << endl;
    }

    ~Test()
    {
        cout << name << "析构函数" << endl;
    }
};


int main()
{
    weak_ptr<Test> wp1;
    {
        cout << "{" << endl;
        shared_ptr<Test> sp1 = make_shared<Test>("A");
        wp1=sp1;
        cout << "}" << endl;
    }
    // wp1持有的资源已经失效
    if(wp1.expired()) // 如果已经失效则返回true
    {
        cout << "资源已失效!" << endl;
    }else
    {
        shared_ptr<Test> sp2 = wp1.lock();
        sp2.get()->show();
    }
    cout << "主函数执行结束" << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值