关于智能指针

1. 智能指针原理

智能指针的本质是一个类模板,它可以创建任何类型的指针对象,当该指针对象的生命周期结束时,会自动调用析构函数释放其对应的资源。

/// 智能指针实现
template <class T>
class Ptr
{
    public:
        Ptr(T* _ptr): ptr(_ptr) {}
        ~Ptr()
        {
            if (ptr)
            {
                cout << "ptr delete" << endl;
                delete ptr;
                ptr = nullptr;
            }
        }
     private:
        T* ptr;
};

以上是智能指针的简单实现,下面我们来使用这个指针:

int main()
{
    Ptr<int> ptr1(new int(1));
    return 1;
}

执行结果:

ptr delete

在生命周期结束后,ptr1会调用析构函数释放资源。

但是如果再创建一个智能指针指向同一个对象时:

int main()
{
    Ptr<int> ptr1(new int(1));
    Ptr<int> ptr2(ptr1);
    return 1;
}

执行结果:

ptr delete
ptr delete
a.out(27135,0x104987d40) malloc: *** error for object 0x127606810: pointer being freed was not allocated
a.out(27135,0x104987d40) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out

程序会报错,提示:释放的指针没有被分配内存。

根据打印结果可以看出,析构函数被调用了两次,也就是说两个指针指向的对象被释放了两次。

所以这个智能指针存在问题,不能允许多个指针指向同一个对象。

2. auto_ptr

auto_ptr是c++98库中提供的智能指针,该指针对于上面的问题,采取的解决方案是:将原指针拷贝或赋值给新指针时,将原指针设置为nullptr,此时就只有新指针指向目标对象。

#include <iostream>
using namespace std;

int main()
{
    auto_ptr<int> autoPtr1(new int(1));
    auto_ptr<int> autoPtr2(autoPtr1);

    cout << *autoPtr2 << endl;
    cout << *autoPtr1 << endl;
    return 1;
}

执行结果:

1
zsh: segmentation fault  ./a.out

程序在访问原指针指向的对象时报错,因为这个原因,很多公司禁止使用这个智能指针。

3. unique_ptr

unique_ptr是c++11库中提供的指针,auto_ptr在c++11中被废弃,所以如果用-std=c++11是无法编译通过含有auto_ptr的代码。unique_ptr直接将拷贝构造函数和赋值函数禁用,不允许对该指针进行赋值和拷贝。

#include <iostream>
using namespace std;

int main()
{
    unique_ptr<int> uniquePtr1(new int(1));
    unique_ptr<int> uniquePtr2(uniquePtr1);
    return 1;
}

编译报错:

main.cpp:26:18: error: call to implicitly-deleted copy constructor of 'unique_ptr<int>'
        unique_ptr<int> uniquePtr2(uniquePtr1);

4. shared_ptr

shared_ptr也是c++11库中提供的智能指针,它允许多个指针指向同一个对象。并且会保证对象资源只会被释放一次。它的实现原理是在shared_ptr内部维护一个被引用计数器,被当目标对象被引用一次时,计数器加一,当引用减少一次时,计数器减一,当计数器为0时,调用析构函数,释放资源。

简单实现代码:

//todo

当然这个指针也存在一定的问题,当两个指针互相引用的时候,会出现计数器无法减到0,因此无法释放资源的问题。

#include<iostream>
using namespace std;

class B;
class A
{
    public:
        ~A()
        {
            cout << "A delete " << endl;
        }

        void setPtr(shared_ptr<B> _ptr)
        {
            ptr = _ptr;
        }

    private:
        shared_ptr<B> ptr;
};

class B
{
    public:
        ~B()
        {
            cout << "B delete " << endl;
        }

        void setPtr(shared_ptr<A> _ptr)
        {
            ptr = _ptr;
        }

    private:
        shared_ptr<A> ptr;
};

int main()
{
    shared_ptr<A> spA(new A());
    shared_ptr<B> spB(new B());
    spA->setPtr(spB);
    spB->setPtr(spA);
    cout << "A.count: " << spA.use_count() << endl;
    cout << "B.count: " << spB.use_count() << endl;

    return 1;
}

执行结果:

A.count:2
B.count:2

从执行结果可以看出,程序并没用调用析构函数释放资源。想要解决这个问题,需要引入weak_ptr。

weak_ptr是为了配合shared_ptr而引人的一种智能指针,它在构造和析构的时候不会引起shared_ptr引用计数器增加或减少。

#include<iostream>
using namespace std;

class B;
class A
{
    public:
        ~A()
        {
            cout << "A delete " << endl;
        }

        void setPtr(shared_ptr<B> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<B> ptr;
};

class B
{
    public:
        ~B()
        {
            cout << "B delete " << endl;
        }

        void setPtr(shared_ptr<A> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<A> ptr;
};

int main()
{
    shared_ptr<A> spA(new A());
    shared_ptr<B> spB(new B());
    spA->setPtr(spB);
    spB->setPtr(spA);
    cout << "A.count: " << spA.use_count() << endl;
    cout << "B.count: " << spB.use_count() << endl;

    return 1;
}

执行结果:

A.count:1
B.count:1
B delete
A delete

从执行结果看,引用计数器没有增加,生命周期结束后,资源被释放。

还有个问题,当智能指针指向一个数组的时候,因为share_ptr在释放资源时,默认调用的是:delete ptr,而不是delete[] ptr,所以此时程序会报错。

#include<iostream>
using namespace std;

class B;
class A
{
    public:
        ~A()
        {
            cout << "A delete " << endl;
        }

        void setPtr(shared_ptr<B> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<B> ptr;
};

class B
{
    public:
        ~B()
        {
            cout << "B delete " << endl;
        }

        void setPtr(shared_ptr<A> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<A> ptr;
};

int main()
{
    shared_ptr<A> spA(new A[10]);

    return 1;
}

执行结果:

A delete
a.out(27771,0x100f27d40) malloc: *** error for object 0x14c606820: pointer being freed was not allocated
a.out(27771,0x100f27d40) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort      ./a.out

从执行结果来看,释放指向数组的指针时,程序报错。这里需要增加一个删除器,share_ptr自身提供了一个可以定制的删除器,用来自定义删除方法。

#include<iostream>
using namespace std;

class B;
class A
{
    public:
        ~A()
        {
            cout << "A delete " << endl;
        }

        void setPtr(shared_ptr<B> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<B> ptr;
};

class B
{
    public:
        ~B()
        {
            cout << "B delete " << endl;
        }

        void setPtr(shared_ptr<A> _ptr)
        {
            ptr = _ptr;
        }

    private:
        weak_ptr<A> ptr;
};

template<class T>
class DeleteArray
{
    public:
        void operator() (const T* array)
        {
            cout << "delete array[]" << endl;
            delete[] array;
        }
};


int main()
{
    shared_ptr<A> spA(new A[3], DeleteArray<A>());

    return 1;
}

执行结果:

delete array[]
A delete
A delete
A delete

从执行结果看,程序成功释放指针指向的数组中的所有对象资源。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值