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

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

二、智能指针如何实现

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

三、手写一个智能指针

// SmartPtr.h
#ifndef __SMARTPTR_H_
#define __SMARTPTR_H_

#include <iostream>

using namespace std;

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

    SmartPtr& operator=(SmartPtr<T>& 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<typename T>
SmartPtr<T>::SmartPtr(T *p)
{
    // 与p指向同一块内存, 并将引用计数初始化为1
    _mPtr = p;
    _mpRef = new size_t(1);
}

template<typename T>
SmartPtr<T>::SmartPtr(SmartPtr<T>& other)
{
    _mPtr = other._mPtr;
    other._mpRef++;
    _mpRef = other._mpRef;
}

template<typename T>
SmartPtr<T>::~SmartPtr()
{
    decrRef();
}

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

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

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

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

template<typename T>
T* SmartPtr<T>::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<test> mp1;

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/107583425
今日推荐