文章目录
-
- 简介
- 源码
- 解析
-
- [1. 引用计数的实现方式](#1. 引用计数的实现方式)
- [2. deleter静态方法的赋值时间节点](#2. deleter静态方法的赋值时间节点)
- [3.make_smart的实现方式 与 好处](#3.make_smart的实现方式 与 好处)
- [4. 几种构造函数](#4. 几种构造函数)
-
- [4.1 空构造函数](#4.1 空构造函数)
- [4.2 接收指针的构造函数](#4.2 接收指针的构造函数)
- [4.3 接收指针和删除方法的构造函数 , 以及auto进行模板lambda的编写](#4.3 接收指针和删除方法的构造函数 , 以及auto进行模板lambda的编写)
- [4.4 拷贝构造函数](#4.4 拷贝构造函数)
- [4.5 赋值运算符](#4.5 赋值运算符)
- [5. release函数, 指针的delete 和 设置为nullptr](#5. release函数, 指针的delete 和 设置为nullptr)
- [6. 获取内部变量, 指针和引用](#6. 获取内部变量, 指针和引用)
简介
git地址
: shimachao/smart_ptr说明
: 这是一个仿写shared_ptr
的库, 只有简短的200行, 实现了shared_ptr
的大部分功能, 上手简单, 非常适合新手阅读.本文说明
: 由于本项目没有那么多难点, 就不再像上篇开源代码阅读
中c++源码阅读__ThreadPool__正文阅读一样, 不单独开一个前提知识部分了, 由于本项目比较简单, 大家就当作对shared_ptr
的一次复习. 看一乐呵就行.
源码
此代码在c++20环境下直接可以运行, 源码如下
cpp
#pragma once
#include <functional>
// 模仿shared_ptr实现一个智能指针
template <typename T>
class smart_ptr
{
public:
smart_ptr();
explicit smart_ptr(T*);
smart_ptr(const smart_ptr&);
smart_ptr(T*, std::function<void(T*)>);
smart_ptr& operator=(const smart_ptr&);
T& operator*() const;
T* operator->() const;
~smart_ptr();
// 向bool的类型转换
explicit operator bool() const;
bool unique();
void reset();
void reset(T*);
void reset(T*, std::function<void(T*)>);
T* release();
T* get() const;
private:
// 默认的deleter
static std::function<void(T*)> default_del;
private:
unsigned* m_p_use_count = nullptr;
T* m_pobject = nullptr;
std::function<void(T*)> m_del = default_del;
};
template <typename T>
std::function<void(T*)> smart_ptr<T>::default_del = [](T*p) {delete p; p = nullptr; };
template <typename T, typename... Args>
smart_ptr<T> make_smart(Args&&... args)
{
smart_ptr<T> sp(new T(std::forward<Args>(args)...));
return sp;
}
template <typename T>
smart_ptr<T>::smart_ptr()
:m_pobject(nullptr), m_p_use_count(new unsigned(1))
{
}
template <typename T>
smart_ptr<T>::smart_ptr(T *p)
:m_pobject(p), m_p_use_count(new unsigned(1))
{
}
template <typename T>
smart_ptr<T>::smart_ptr(T *p, std::function<void(T*)> del)
:m_pobject(p), m_p_use_count(new unsigned(1)), m_del(del)
{
}
template <typename T>
smart_ptr<T>::smart_ptr(const smart_ptr& rhs)
:m_pobject(rhs.m_pobject), m_p_use_count(rhs.m_p_use_count), m_del(rhs.m_del)
{
(*m_p_use_count)++;
}
template <typename T>
smart_ptr<T>& smart_ptr<T>::operator =(const smart_ptr &rhs)
{
// 使用rhs的deleter
m_del = rhs.m_del;
// 递增右侧运算对象的引用计数
++(*rhs.m_p_use_count);
// 递减本对象的引用计数
if (--(*m_p_use_count) == 0)
{
// 如果管理的对象没有其他用户了,则释放对象分配的成员
m_del(m_pobject);
delete m_p_use_count;
}
m_p_use_count = rhs.m_p_use_count;
m_pobject = rhs.m_pobject;
return *this; // 返回本对象
}
template <typename T>
T& smart_ptr<T>::operator*() const
{
return *m_pobject;
}
template <typename T>
T* smart_ptr<T>::operator->() const
{
return &this->operator*();
}
template <typename T>
smart_ptr<T>::~smart_ptr()
{
if (--(*m_p_use_count) == 0)
{
m_del(m_pobject);
m_pobject = nullptr;
delete m_p_use_count;
m_p_use_count = nullptr;
}
}
template <typename T>
bool smart_ptr<T>::unique()
{
return *m_p_use_count == 1;
}
template <typename T>
void smart_ptr<T>::reset()
{
(*m_p_use_count)--;
if (*m_p_use_count == 0)
{
m_del(m_pobject);
}
m_pobject = nullptr;
*m_p_use_count = 1;
m_del = default_del;
}
template <typename T>
void smart_ptr<T>::reset(T* p)
{
(*m_p_use_count)--;
if (*m_p_use_count == 0)
{
m_del(m_pobject);
}
m_pobject = p;
*m_p_use_count = 1;
m_del = default_del;
}
template <typename T>
void smart_ptr<T>::reset(T *p, std::function<void(T*)> del)
{
reset(p);
m_del = del;
}
template <typename T>
T* smart_ptr<T>::release()
{
(*m_p_use_count)--;
if (*m_p_use_count == 0)
{
*m_p_use_count = 1;
}
auto p = m_pobject;
m_pobject = nullptr;
return p;
}
template <typename T>
T* smart_ptr<T>::get() const
{
return m_pobject;
}
template <typename T>
smart_ptr<T>::operator bool() const
{
return m_pobject != nullptr;
}
解析
这里会把smart_ptr
类中每一部分都单独拿出来说明, 并进行举例
1. 引用计数的实现方式
我们可以看到他的实现方式
cpp
unsigned* m_p_use_count = nullptr;
使用的是整数的指针, 如果不使用指针的话, 那么是打不到公用引用计数的效果的, 如下
cpp
int* a1 = new int(1);
int* a2 = a1;
*a2 += 1;
cout << *a1 << endl;
cout << *a2 << endl;
int a3 = 1;
int a4 = a3;
a3 += 1;
cout << a4 << endl;
cout << a4 << endl;
int a5 = 1;
int& a6 = a5;
a5++;
cout << a5 << endl;
cout << a6 << endl;
执行结果
那么我们是不是也可以用引用&
来实现引用计数的共用呢? 我觉得是可以的
2. deleter静态方法的赋值时间节点
cpp
template <typename T>
class smart_ptr
{
private:
// 默认的deleter
static std::function<void(T*)> default_del;
std::function<void(T*)> m_del = default_del;
}
template <typename T>
std::function<void(T*)> smart_ptr<T>::default_del = [](T*p) {delete p; p = nullptr; };
静态方法的赋值, 是 程序启动并进入主函数之前进行赋值的, 具体地这个初始化是在包含这行代码的翻译单元被加载时完成的, 所以 default_del
在任何smart_ptr
的构造函数调用之前就赋值了.
m_del
的赋值是在smart_ptr
的构造函数被调用时赋值的
3.make_smart的实现方式 与 好处
cpp
template <typename T, typename... Args>
smart_ptr<T> make_smart(Args&&... args)
{
smart_ptr<T> sp(new T(std::forward<Args>(args)...));
return sp;
}
这里右值引用
和完美转发
相关的知识在左值右值, 左值引用右值引用,完美转发这篇博客中有详细介绍.
这里和shared_ptr
一样, 也是接收目标类的构造函数的参数, 直接返回智能指针的实现方式
好处
: 我们使用make_smart
的方式, 创建智能指针非常好, 因为不用我们手动new
一个指针出来, 那么智能指针外部就没有该指针的变量, 就不会造成一些未知错误, 保证了在指针的生命周期内, 都是被智能指针安全管理的.
4. 几种构造函数
常用的构造函数和赋值运算符 在这篇文章里有介绍
C++基础知识,对象移动,拷贝构造函数,移动拷贝构造函数,赋值运算符,移动赋值运算符
4.1 空构造函数
cpp
template <typename T>
smart_ptr<T>::smart_ptr()
:m_pobject(nullptr), m_p_use_count(new unsigned(1))
{}
这个方法是构造一个内容为空的智能指针, 声明方式如下
smart_prt<MyClass> sp();
这个可以配合后面的reset
方法使用, 给智能指针重新赋值
4.2 接收指针的构造函数
cpp
template <typename T>
smart_ptr<T>::smart_ptr(T *p)
:m_pobject(p), m_p_use_count(new unsigned(1))
{}
使用方式如下, 但是不推荐这种方式, 因为外部有了指针变量s1
, 这就给智能指针的管理带来了未知的风险
cpp
MyStruct* s1 = new MyStruct(1, 2);
smart_ptr<MyStruct> sp(s1);
4.3 接收指针和删除方法的构造函数 , 以及auto进行模板lambda的编写
cpp
template <typename T>
smart_ptr<T>::smart_ptr(T *p, std::function<void(T*)> del)
:m_pobject(p), m_p_use_count(new unsigned(1)), m_del(del)
{}
这个构造方法就是上面4.2
多加一个参数, 没什么好说的, 但是其中delete方法的传入, 我们可以通过auto
来编写模板lambda, 这是c++新的特性, 如下
cpp
auto deleter = [](auto* p) {delete *p; p = nullptr;};
MyStruct* s = new MyStruct(1, 2);
smart_ptr<MyStruct> sp(s, deleter);
这样, 我们就不用template
写一大堆, 而直接构造出了一个模板函数
4.4 拷贝构造函数
拷贝构造函数, 会造成引用计数+1, 两个智能指针指向的是同一块地址
cpp
template <typename T>
smart_ptr<T>::smart_ptr(const smart_ptr& rhs) :m_pobject(rhs.m_pobject), m_p_use_count(rhs.m_p_use_count), m_del(rhs.m_del)
{
(*m_p_use_count)++;
}
=
符号的赋值, 是赋值的内容, 不是变量本身的地址设置为一致(这是引用的=
), 所以指针的=
, 是指针变量内指向的地址设置为相同
cpp
MyStruct* s1 = new MyStruct(1, 2);
smart_ptr<MyStruct> sp(s1);
smart_ptr<MyStruct> sp2(sp);
cout << std::boolalpha;
cout << (sp.get() == sp2.get()) << endl;
执行结果
4.5 赋值运算符
赋值运算符 和 拷贝构造函数就大不一样了, 赋值运算符会把等号左边
的智能指针内的变量引用计数-1, 如果引用计数为0了, 还会释放资源
cpp
template <typename T>
smart_ptr<T>& smart_ptr<T>::operator =(const smart_ptr &rhs)
{
// 使用rhs的deleter
m_del = rhs.m_del;
// 递增右侧运算对象的引用计数
++(*rhs.m_p_use_count);
// 递减本对象的引用计数
if (--(*m_p_use_count) == 0)
{
// 如果管理的对象没有其他用户了,则释放对象分配的成员
m_del(m_pobject);
delete m_p_use_count;
}
m_p_use_count = rhs.m_p_use_count;
m_pobject = rhs.m_pobject;
return *this; // 返回本对象
}
所以根据代码, 我们合理预测, 如果=
左边的智能指针引用计数如果是1的话, 那么使用了赋值运算符, 就会造成内部的指针析构
测试代码
cpp
struct MyStruct
{
MyStruct() = default;
MyStruct(int a, int b) :a(a), b(b) {}
~MyStruct() {
cout << "~MyStruct (" << a << "," << b << ")" << endl;
}
int a;
int b;
};
int main()
{
smart_ptr<MyStruct> sp1 = make_smart<MyStruct>(1, 2);
smart_ptr<MyStruct> sp2 = make_smart<MyStruct>(3, 4);
sp1 = sp2;
cout << "==========" << endl;
return 0;
}
执行结果
可以看到, 是sp1
在程序结束之前析构的
5. release函数, 指针的delete 和 设置为nullptr
这里我们要明确,delete
是把指针指向的内容进行析构, 而直接把指针设置为nullptr
, 只是把当前这个指针变量设置为nullptr
, 对于指针指向的内容, 不做任何处理
比如release函数
cpp
template <typename T>
T* smart_ptr<T>::release()
{
(*m_p_use_count)--;
if (*m_p_use_count == 0)
{
*m_p_use_count = 1;
}
auto p = m_pobject;
m_pobject = nullptr;
return p;
}
在这个函数里, 只是把m_pojbect
这个指针变量本身设置为了nullptr
, 而指针指向的变量没有做任何操作, 最后把这个指针变量copy一份, 返回出去了
测试代码
cpp
struct MyStruct
{
MyStruct() = default;
MyStruct(int a, int b) :a(a), b(b) {}
~MyStruct() {
cout << "~MyStruct (" << a << "," << b << ")" << endl;
}
int a;
int b;
};
int main()
{
smart_ptr<MyStruct> sp1 = make_smart<MyStruct>(1, 2);
MyStruct* s1 = sp1.release();
cout << "=========" << endl;
delete s1;
return 0;
}
执行结果
可以看到, release确实没有造成析构
6. 获取内部变量, 指针和引用
有两个方法, 一个是重载的操作符*
, 一个是get
方法
只不过get
获取的指针
*
获取的是引用
cpp
template <typename T>
T& smart_ptr<T>::operator*() const
{
return *m_pobject;
}
template <typename T>
T* smart_ptr<T>::get() const
{
return m_pobject;
}
测试代码
cpp
int main()
{
smart_ptr<MyStruct> sp1 = make_smart<MyStruct>(1, 2);
cout << std::boolalpha << endl;
// (*sp1)获取了引用: &MyStruct, 转为指针 *(&MyStruct)
cout << (&(*sp1) == sp1.get()) << endl;
return 0;
}
执行结果