简 述: C++11 智能指针的深入分析,和动手实现简版的智能指针 std::shared_ptr
、std::unique_ptr
文章目录
- 背景
- std::shared_ptr
- 原理
- 代码
- reference
- std::unique_ptr
- 原理
- 代码
- reference
- 系列
本文初发于 “偕臧的小站”,同步转载于此。
背景
实现原理提前需要理解 特殊成员函数、std::exchange() C++14
、std::swap()
、 std::move()
、constexpr
、explicit
、noexcept
等,若是遗忘可参考此文
- 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 <iostream>
#include <mutex>
using namespace std;template <typename T>
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<T>& obj) // 其 _refCount 可以通过另外一个指针来修改,指向的是同一个地址: _ptr(obj._ptr), _refCount(obj._refCount), _pMutex(obj._pMutex){cout << "copy constructor" << endl;addRefCount();};SharedPtr<T>& operator=(const SharedPtr<T>& 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<T>&& obj) noexcept;//SharedPtr<T>& operator=(SharedPtr<T>&& 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<int> sp1(new int(10));SharedPtr<int> sp2(sp1);*sp2 = 20;//sp1 与 sp2 在管理这部分资源,引用计数为 2cout << sp1.useCount() << " *ptr:" << *sp1 << endl; //2 20cout << sp2.useCount() << " *ptr:" << *sp2 << endl; //2 20SharedPtr<int> 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 <iostream>
using namespace std;template <typename T>
class UniquePtr
{
public:constexpr UniquePtr() : _ptr(nullptr) { cout << "default constructor" << endl; };explicit UniquePtr(T* obj) : _ptr(obj) { cout << "no default constructor" << endl; };UniquePtr(UniquePtr<T>&& obj) noexcept : _ptr(obj._ptr) {cout << "move constructor" << endl;obj._ptr = nullptr;}UniquePtr<T>& operator=(UniquePtr<T>&& obj) noexcept{cout << "move assignment constructor" << endl;if (&obj != this) {if (obj._ptr) {_ptr = obj._ptr;obj._ptr = nullptr;}}return *this;}UniquePtr(const UniquePtr<T>& obj) = delete; // C++11 delete 禁止方式,C++98 用 private 来隐藏UniquePtr<T>& operator=(const UniquePtr<T>& 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<T>& obj){std::swap(_ptr, obj._ptr);}private:T* _ptr;
};int main()
{UniquePtr<int> up1(new int(10));cout << "up1:" << up1.get() << " *ptr:" << *up1 << endl;UniquePtr<int> up2(std::move(up1)); // 控制权变更cout << "up1:" << up1.get() << endl; // nullptr, 此时 up1 已无控制权cout << "up2:" << up2.get() << " *ptr:" << *up2 << endl;UniquePtr<int> up3(new int(30));UniquePtr<int> 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
学习,附学习由浅入深的目录。