C和C++程序员面试秘笈:29---你知道智能指针底层的原理是什么吗?能手写一个智能指针吗?

一、C++标准库的智能指针

  • 标准库提供了3种智能指针:
    • shared_ptr:https://blog.csdn.net/qq_41453285/article/details/105438328
    • weak_ptr:https://blog.csdn.net/qq_41453285/article/details/105438426
    • unique_ptr:https://blog.csdn.net/qq_41453285/article/details/105438477
  • 各种语法本文就不介绍了,查看上面的链接

二、智能指针如何实现

  • 本文主要介绍shared_ptr是如何实现的,我们知道在代码中可以让多份实例指向于同一个shared_ptr对象
  • 智能指针主要是靠一个“引用计数”来实现的:
    • 每次创建类的新对象时,初始化指针并将引用计数置为1
    • 当对象作为另一个对象的副本而创建时,拷贝构造函数拷贝指针并增加与之对应的引用计数
    • 当一个对象进行赋值时,赋值操作符减少左操作符所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作符所指对象的引用计数
    • 调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象)
    • 重载"->"以及"*"操作符,使得智能指针有类似于普通指针的操作

三、手写一个智能指针

  • 源码链接请参阅:https://github.com/dongyusheng/csdn-code/tree/master/C%2B%2B/SmartPtr
  • 实现如下:
// SmartPtr.h
#ifndef __SMARTPTR_H_
#define __SMARTPTR_H_

#include 

using namespace std;

template
class SmartPtr
{
public:
    SmartPtr(T *p = 0);         // 构造函数
    SmartPtr(SmartPtr& other);  // 拷贝构造函数
    ~SmartPtr();                // 析构函数

    SmartPtr& operator=(SmartPtr& rhs); // 赋值运算符

    T& operator*();
    T* operator->();
private:
    // 将对象的引用计数减1
    void decrRef()
    {
        // 如果引用计数减1之后变为0, 则释放该智能指针的底层数据, 此智能指针不再继续使用
        if(--*_mpRef == 0)
        {
            delete _mPtr;
            delete _mpRef;
        }
    }
private:
    T *_mPtr;       // 保存数据对象指针
    size_t *_mpRef; // 当前数据对象的引用计数
};

template
SmartPtr::SmartPtr(T *p)
{
    // 与p指向同一块内存, 并将引用计数初始化为1
    _mPtr = p;
    _mpRef = new size_t(1);
}

template
SmartPtr::SmartPtr(SmartPtr& other)
{
    _mPtr = other._mPtr;
    other._mpRef++;
    _mpRef = other._mpRef;
}

template
SmartPtr::~SmartPtr()
{
    decrRef();
}

template
SmartPtr& SmartPtr::operator=(SmartPtr& rhs)
{
    // 将自身的引用计数减1
    decrRef();

    // 指向于参数所指的对象, 并将参数所指对象的引用计数+1, 并指向其引用计数
    _mPtr = rhs._mPtr;
    rhs._mpRef++;
    _mpRef = rhs._mpRef;
    
    return *this;
}

template
T& SmartPtr::operator*()
{
    if(_mPtr)
        return *_mPtr;

    throw std::runtime_error("dereference of  NULL pointer");
}

template
T* SmartPtr::operator->()
{
    if(_mPtr)
        return _mPtr;

    throw std::runtime_error("access rgeough NULL pointer");
}

#endif // __SMARTPTR_H_
  • 测试代码如下:
// main.cpp
#include "SmartPtr.h"

class test
{
public:
    test(char *_name = ""): name(_name) {}

    void showName() { cout << name << endl; }
private:
    string name;
};

int main()
{
    SmartPtr mp1;

    SmartPtr mp2(new test("C++"));
    SmartPtr mp3(mp2);
    mp2->showName();
    mp3->showName();
    std::cout << std::endl;

    SmartPtr mp4(new test("Linux"));
    mp3 = mp4;
    mp3->showName();
    mp4->showName();
    std::cout << std::endl;

    *mp3 = "Golang";
     mp3->showName();
    
    return 0;
}

C和C++程序员面试秘笈:29---你知道智能指针底层的原理是什么吗?能手写一个智能指针吗?_第1张图片

你可能感兴趣的:(C和C++程序员面试秘笈)