C++ 实现智能指针:shared_ptr 和 unique_ptr

简 述: C++11 智能指针的深入分析,和动手实现简版的智能指针 std::shared_ptrstd::unique_ptr

文章目录

    • 背景
    • std::shared_ptr
      • 原理
      • 代码
      • reference
    • std::unique_ptr
      • 原理
      • 代码
      • reference
    • 系列


本文初发于 “偕臧的小站”,同步转载于此。


背景

实现原理提前需要理解 特殊成员函数std::exchange() C++14std::swap()std::move()constexprexplicitnoexcept 等,若是遗忘可参考此文

  • C++ 类的六个特殊成员函数
  • C++ 11/14/17 的新特性 占位

最后,Demo 实现或许不够十分完美和严谨,但对于其理解智能指针的原理和面试手写实现时候,足够。若有纰漏,请指正。


std::shared_ptr

原理

  • shared_ptr 的原理: 通过引用计数的方式来实现多个 shared_ptr 对象之间共享资源。

  • 通过引用计数和模板来实现 shared_ptr;构造函数定义的时候,要初始化其指针、引用计数、和 mutex

  • “copy assignment constructor” 除了校验是否相等、是否为空的时候、拷贝时要先释放旧资源,旧的引用计数 -1,赋值后再指向对新的资源的引用计数 +1

  • 释放资源时,要先校验是否存在,及计数为 0 才释放;


代码

💻 win10 22H2 📎 Visual Studio 2019 📎 C++11 见 SharedPtr.h

/******************************************************************** Copyright (c) 2022~-023 XMuli  All rights reserved.* Description: C++ 实现一个核心的 shared_ptr 智能指针模板类******************************************************************/
#pragma once
#include 
#include 
using namespace std;template 
class SharedPtr
{
public:SharedPtr() : _ptr(nullptr), _refCount(nullptr), _pMutex(nullptr) { cout << "default constructor" << endl; };SharedPtr(T* obj) : _ptr(obj), _refCount(new int(1)), _pMutex(new mutex) { cout << "no default constructor" << endl; };SharedPtr(const SharedPtr& obj)  // 其 _refCount 可以通过另外一个指针来修改,指向的是同一个地址: _ptr(obj._ptr), _refCount(obj._refCount), _pMutex(obj._pMutex){cout << "copy constructor" << endl;addRefCount();};SharedPtr& operator=(const SharedPtr& obj){cout << "copy assignment constructor" << endl;if (&obj != this) {if (_ptr != obj._ptr) {release();            // 先释放旧的资源_ptr = obj._ptr;_refCount = obj._refCount;_pMutex = obj._pMutex;addRefCount();        // 再技计数 +1}}return *this;}//SharedPtr(SharedPtr&& obj) noexcept;//SharedPtr& operator=(SharedPtr&& obj)noexcept;~SharedPtr() { cout << "destructor" << endl; release(); }T& operator*() { return *_ptr; }T* operator->() { return _ptr; }int useCount() { return *_refCount; }T* get() { return _ptr; }private:void addRefCount(){cout << "addRefCount" << endl;_pMutex->lock();++*_refCount;_pMutex->unlock();}void release(){cout << "release" << endl;bool bDelMutex = false;_pMutex->lock();if (_ptr && --*_refCount == 0) {  // 先校验是否存在,及计数为 0 才释放delete _ptr;delete _refCount;_ptr = nullptr;_refCount = nullptr;bDelMutex = true;}_pMutex->unlock();if (bDelMutex)delete _pMutex;}private:                  // 需在构造函数中初始化T* _ptr;              // 指向管理资源的指针int* _refCount;       // 引用计数mutex* _pMutex;       // 计数自增非原子操作,加锁解决多线程
};int main()
{SharedPtr sp1(new int(10));SharedPtr sp2(sp1);*sp2 = 20;//sp1 与 sp2 在管理这部分资源,引用计数为 2cout << sp1.useCount() << "  *ptr:" << *sp1 << endl;		//2	 20cout << sp2.useCount() << "  *ptr:" << *sp2 << endl;		//2	 20SharedPtr sp3(new int(30));                              sp2 = sp3;		                                            //sp3 赋值给它,释放管理的旧资源,引用计数-1,   cout << sp1.useCount() << "  *ptr:" << *sp1 << endl;        //1	 20cout << sp2.useCount() << "  *ptr:" << *sp2 << endl;        //2	 30cout << sp3.useCount() << "  *ptr:" << *sp3 << endl;        //2	 30sp1 = sp3;                                                    cout << sp1.useCount() << "  *ptr:" << *sp1 << endl;        //3	 30cout << sp2.useCount() << "  *ptr:" << *sp2 << endl;        //3	 30cout << sp3.useCount() << "  *ptr:" << *sp3 << endl;        //3	 30std::cout << "Hello World!\n";return 0;
}/*****************************打印结果*******************************
no default constructor
copy constructor
addRefCount
2  *ptr:20
2  *ptr:20
no default constructor
copy assignment constructor
release
addRefCount
1  *ptr:20
2  *ptr:30
2  *ptr:30
copy assignment constructor
release
addRefCount
3  *ptr:30
3  *ptr:30
3  *ptr:30
Hello World!
destructor
release
destructor
release
destructor
release******************************************************************/

Note:

  • mutex 实现了引用计数是线程安全的。但智能指针管理的对象存放在上,两个线程中同时去访问,会导致线程安全问题。

  • 书写测试时,若使用默认构造函数, 成员变量 _ptr、_refCount、_pMutex 在 release() 中容易崩溃;推荐带参的构造函数,完美运行测试


reference

  • C++ 智能指针与底层实现剖析
  • 面试题:简单实现一个shared_ptr智能指针
  • C++智能指针: shared_ptr 实现详解

std::unique_ptr

原理

  • unique_ptr的设计思路非常的粗暴:防拷贝,也就是不让拷贝和赋值
  • unique_ptr 唯一 拥有其所指对象,同一时刻只能有一个unique_ptr 指向给定对象(通过禁止拷贝语义、只有移动语义来实现

代码

💻 win10 22H2 📎 Visual Studio 2019 📎 C++11 见 UniquePtr.h

/******************************************************************** Copyright (c) 2022~2023 XMuli  All rights reserved.* Description: C++ 实现一个核心的 unique_ptr 智能指针模板类;******************************************************************/
#pragma once
#include 
using namespace std;template 
class UniquePtr
{
public:constexpr UniquePtr() : _ptr(nullptr) { cout << "default constructor" << endl; };explicit  UniquePtr(T* obj) : _ptr(obj) { cout << "no default constructor" << endl; };UniquePtr(UniquePtr&& obj) noexcept : _ptr(obj._ptr) {cout << "move constructor" << endl;obj._ptr = nullptr;}UniquePtr& operator=(UniquePtr&& obj) noexcept{cout << "move assignment constructor" << endl;if (&obj != this) {if (obj._ptr) {_ptr = obj._ptr;obj._ptr = nullptr;}}return *this;}UniquePtr(const UniquePtr& obj) = delete;		// C++11 delete 禁止方式,C++98 用 private 来隐藏UniquePtr& operator=(const UniquePtr& obj) = delete;~UniquePtr(){cout << "destructor" << endl;if (_ptr) {delete _ptr;_ptr = nullptr;}}T& operator*() const { return *_ptr; }T* operator->() const { return _ptr; }T* get() const { return _ptr; }T* release()            // return std::exchange(_ptr, nullptr); // C++14{T* temp = _ptr;_ptr = nullptr;return temp;}void reset(T* ptr)		// std::exchange(_ptr, ptr); // C++14{_ptr = ptr;}void swap(UniquePtr& obj){std::swap(_ptr, obj._ptr);}private:T* _ptr;
};int main()
{UniquePtr up1(new int(10));cout << "up1:" << up1.get() << "  *ptr:" << *up1 << endl;UniquePtr up2(std::move(up1));                 // 控制权变更cout << "up1:" << up1.get() << endl;		        // nullptr, 此时 up1 已无控制权cout << "up2:" << up2.get() << "  *ptr:" << *up2 << endl;UniquePtr up3(new int(30));UniquePtr up4(new int(40));cout << "up3:" << up3.get() << "  *ptr:" << *up3 << endl;cout << "up4:" << up4.get() << "  *ptr:" << *up4 << endl;up3 = std::move(up2);                               // 控制权变更cout << "up3:" << up3.get() << "  *ptr:" << *up3 << endl;cout << "up4:" << up4.get() << "  *ptr:" << *up4 << endl;up3.swap(up4);cout << "up3:" << up3.get() << "  *ptr:" << *up3 << endl;cout << "up4:" << up4.get() << "  *ptr:" << *up4 << endl;up3.release();cout << "up3:" << up3.get() << endl;std::cout << "Hello World!\n";return 0;
}/*****************************打印结果*******************************
no default constructor
up1:0086DEB8  *ptr:10
move constructor
up1:00000000
up2:0086DEB8  *ptr:10
no default constructor
no default constructor
up3:008656D0  *ptr:30
up4:00865700  *ptr:40
move assignment constructor
up3:0086DEB8  *ptr:10
up4:00865700  *ptr:40
up3:00865700  *ptr:40
up4:0086DEB8  *ptr:10
up3:00000000
Hello World!
destructor
destructor
destructor
destructor******************************************************************/

reference

  • 二、C++实现unique_ptr
  • 面试官的动机——实现智能指针1:unique_ptr
  • C++进阶:智能指针之unique_ptr

系列

QtExamples 【Studio】

欢迎 star ⭐ 和 fork 🍴这个系列的 C++ / QT / DTK 学习,附学习由浅入深的目录。