浅谈:智能指针C++

本篇博客,我将对为什么引入智能指针?智能指针的设计原理以及作用是什么?常用的智能指针有哪些?它是如何实现的来进行浅谈!

一、智能指针的引入

  • 我们先来看一个例子(如下图):
    浅谈:智能指针C++_第1张图片

  • 于是,师徒二人展开激烈的讨论:
    浅谈:智能指针C++_第2张图片

  • 此时我们就知道了为什么会有智能指针的存在。

二、智能指针的实现原理

1.概念:

  • 智能指针是一个类,它使用了RAII技术,在该类的构造函数中传入一个普通指针,析构函数中释放传入的指针。

2.几点说明:

(1)RAII(Resource Acquisition Is Initialization)–资源分配即初始化,RAII是指:定义一个类来封装资源的分配与释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理,它保证了资源正确的初始化和释放。
(2)智能指针不是指针,它实际上是一个模板,由智能指针实例化出来的对象具有和常规指针相似的行为,重点是智能指针负责自动的释放所指对象。
(3)由于智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。

三、智能指针的作用

  • 以下三点学习理解智能指针:

(1)从较浅的层面看,智能指针是利用了一种叫做RAII(资源分配即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。

(2)智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有讲究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。

(3)智能指针还有一个作用是把值语义转换成引用语义。(这个是我在别的地方看到的,分享分享!)
C++和Java有一处最大的区别在于语义不同,在Java里面下列代码:
  

Animal a = new Animal();
Animal b = a
//这里只生成了一个对象

在C++中:

  Animal a;
  Animal b = a;
  //这里生成了两个对象

四、常用的智能指针(C++11标准库)

1.auto_ptr
  • 功能:指针管理权的转移, 是C++中最早的智能指针,存在很大问题。
  • 不要使用!原因是:不支持复制(拷贝构造函数)和赋值(operator =),但复制或赋值的时候不会提示出错。因为不能被复制,所以不能被放入容器中。
  • auto_ptr是通过权限转移的方式来防止由浅拷贝所带来的问题,所谓权限转移就是说开辟的内存在任何时刻都只能由一个指针指向。

还有个类似的智能指针:unique_ptr, 也不支持复制和赋值,但比auto_ptr好,直接赋值会编译出错。实在想赋值的话,需要使用:std::move。

2.scoped_ptr
  • 概念:也称为防拷贝智能指针,它通过将赋值运算符的重载函数、拷贝构造函数声明设置为私有函数,只声明不定义,既防止类外的人为定义,同时阻止了拷贝。
  • scoped_array是用来管理数组的。
3.shared_ptr
  • 概念:通过引用计数实现的智能指针,功能强大,使用以及适用场景多。

  • 通过引用计数来控制空间的释放,当一块空间创建时引用计数为1,当有新的指针指向这块空间时,引用计数加1,反之减1,直到引用计数减到0时才真的释放这块空间。

  • shared_array用来管理数组的。
  • shared_ptr的缺点:

引用计数存在线程安全问题和循环引用问题
例如用智能指针实现一个双向链表,双向链表中的前驱和后继指针用的均是智能指针,则在销毁链表的时候,由于两个结点的销毁都依赖于对方先销毁,这时就会出现错误。
为了解决循环引用问题,我们可以使用weak_ptr,weak_ptr不增加引用计数。

4.weak_ptr
  • 概念:为了解决shared_ptr指针的循环使用缺陷。

  • 为什么不用原生指针替换weak_ptr?

弱引用并不修改该对象的引用计数,这意味着弱引用它并不对对象的内存进行管理。在功能上类似于普通指针,然而一个较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存

五、智能指针的实现

  • 如下是基于引用计数的智能指针实现,需要实现构造、析构、拷贝构造、=操作符重载、重载*-和>操作符
template <typename T>
class SmartPointer {
public:
    //构造函数
    SmartPointer(T* p=0): _ptr(p), _reference_count(new size_t){
        if(p)
            *_reference_count = 1; 
        else
            *_reference_count = 0; 
    }
    //拷贝构造函数
    SmartPointer(const SmartPointer& src) {
        if(this!=&src) {
            _ptr = src._ptr;
            _reference_count = src._reference_count;
            (*_reference_count)++;
        }
    }
    //重载赋值操作符
    SmartPointer& operator=(const SmartPointer& src) {
        if(_ptr==src._ptr) {
            return *this;
        }
        releaseCount();
        _ptr = src._ptr;
        _reference_count = src._reference_count;
        (*_reference_count)++;
        return *this;
    }

    //重载操作符
    T& operator*() {
        if(ptr) {
            return *_ptr;
        }
        //throw exception
    }
    //重载操作符
    T* operator->() {
        if(ptr) {
            return _ptr;
        }
        //throw exception
    }
    //析构函数
    ~SmartPointer() {
        if (--(*_reference_count) == 0) {
            delete _ptr;
            delete _reference_count;
        }
    }
private:
    T *_ptr;
        size_t *_reference_count;
        void releaseCount() {
        if(_ptr) {
            (*_reference_count)--;
                if((*_reference_count)==0) {
                    delete _ptr;
                    delete _reference_count;
                }
        }
        }
};

int main() 
{
    SmartPointer<char> cp1(new char('a'));
    SmartPointer<char> cp2(cp1);
    SmartPointer<char> cp3;
    cp3 = cp2;
    cp3 = cp1;
    cp3 = cp3;
    SmartPointer<char> cp4(new char('b'));
    cp3 = cp4;
}

你可能感兴趣的:(C++)