## 智能指针实现原理
智能指针本质上并不神秘,其实就是 RAII 资源管理功能而已,下面代码就是比较典型的 RAII 资源管理类:
~~~
class shape {
public:
virtual ~shape() {}
};
class circle : public shape {
public:
~circle() { puts("~circle"); }
}
~~~
~~~
class shape_wrapper {
public:
explicit shape_wrapper(shape* ptr = nullptr)
: ptr_(ptr) {}
~shape_wrapper() { delete ptr_; }
shape* get() const { return ptr_; }
private:
shape* ptr_;
};
~~~
这个类可以完成智能指针最基本的功能,对超出作用域的对象进行释放。但该类有以下缺点:
1. 这个类只适用于 shape 类;
2. 该类对象的行为不够像指针;
3. 拷贝该类对象会引发程序异常。
## auto_ptr 指针实现
~~~
template <typename T>
class Auto_ptr {
public:
explicit Auto_ptr(T* ptr = nullptr)
: ptr_(ptr) {}
~Auto_ptr() { delete ptr_; }
T* get() const { return ptr_; }
/*
* 能用 * 运算符解引用
* 能用 -> 运算符指向对象成员
* 能像指针一样用在布尔表达式里
*/
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
Auto_ptr(Auto_ptr& other) {
ptr_ = other.release();
}
Auto_ptr& operator=(Auto_ptr& rhs) {
Auto_ptr(rhs).swap(*this);
return *this;
}
T* release() {
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(Auto_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
}
private:
T* ptr_;
}
~~~
~~~
Auto_ptr<shape> ptr1(new circle);
Auto_ptr<shape> ptr2{ptr1}; // OK
Auto_ptr<shape> ptr3;
ptr3 = ptr1; // OK
ptr3 = std::move(ptr1); // 编译出错
Auto_ptr<shape> ptr4{std::move(ptr3)}; // 编译出错
~~~
## unique\_ptr 实现
~~~
template <typename T>
class Unique_ptr {
public:
explicit Unique_ptr(T* ptr = nullptr)
: ptr_(ptr) {}
~Unique_ptr() { delete ptr_; }
T* get() const { return ptr_; }
/*
* 能用 * 运算符解引用
* 能用 -> 运算符指向对象成员
* 能像指针一样用在布尔表达式里
*/
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
Unique_ptr(Unique_ptr&& other) {
ptr_ = other.release();
}
Unique_ptr& operator=(Unique_ptr rhs) {
rhs.swap(*this);
return *this;
}
T* release() {
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(Unique_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
}
private:
T* ptr_;
~~~
~~~
Unique_ptr<shape> ptr1(new circle);
Unique_ptr<shape> ptr2{ptr1}; // 编译出错
Unique_ptr<shape> ptr3;
ptr3 = ptr1; // 编译出错
ptr3 = std::move(ptr1); // OK
Unique_ptr<shape> ptr4{std::move(ptr3)}; // OK
~~~
## shared_ptr实现
~~~
class shared_count {
public:
shared_count() : count_(1) {}
long add_count() { return ++count_; }
long reduce_count() { return --count_; }
long get_count() const { return count_; }
private:
long count_;
};
template <typename T>
class Shared_ptr {
public:
/*
* 构造函数跟之前的主要不同点是会构造一个 shared_count 出来。
* 析构函数在看到 ptr_ 非空时(此时根据代码逻辑,shared_count
* 也必然非空),需要对引用数减一,并在引用数降到零时彻底删除
* 对象和共享计数。原理就是这样,不复杂。
*/
explicit Shared_ptr(T* ptr = nullptr)
: ptr_(ptr) {
if (ptr) {
shared_count_ = new shared_count();
}
}
~Shared_ptr() {
if (ptr_ && !shared_count_->reduce_count()) {
delete ptr_;
delete shared_count_;
}
}
T* get() const { return ptr_; }
/*
* 能用 * 运算符解引用
* 能用 -> 运算符指向对象成员
* 能像指针一样用在布尔表达式里
*/
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
/*
* 除复制指针之外,对于拷贝构造的情况,我们需要在指针非空时
* 把引用数加一,并复制共享计数的指针。对于移动构造的情况,
* 我们不需要调整引用数,直接把 other.ptr_ 置为空,认为
* other 不再指向该共享对象即可。
*/
Shared_ptr(Shared_ptr& other) {
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
template <typename U>
Shared_ptr(Shared_ptr<U>& other) {
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
template <typename U>
Shared_ptr(Shared_ptr<U>&& other) {
ptr_ = other.ptr_;
if (ptr_) {
shared_count_ = other.shared_count_;
other.shared_count_ = nullptr;
}
}
Shared_ptr& operator=(Shared_ptr rhs) {
rhs.swap(*this);
return *this;
}
void swap(Shared_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
swap(shared_count_, rhs.shared_count_);
}
long use_count() const {
return ptr_ ? shared_count_->get_count() : 0;
}
private:
T* ptr_;
shared_count* shared_count_;
}
~~~
~~~
Shared_ptr<shape> ptr1(new circle);
printf("use count of ptr1 is %ld\n", ptr1.use_count());
Shared_ptr<shape> ptr2;
printf("use count of ptr2 is %ld\n", ptr2.use_count());
ptr2 = ptr1;
printf("use count of ptr2 is %ld\n", ptr2.use_count());
if (ptr1) {
puts("ptr1 not empty");
}
~~~
结果如下:
use count of ptr1 is 1
use count of ptr2 is 0
use count of ptr2 is 2
ptr1 not empty
~circle
## 线程安全的智能指针
用原子对象,我们可以轻而易举地把上面的的 shared\_count 变成线程安全的。
~~~
class shared_count {
public:
shared_count() : count_(1) {}
long add_count() {
count_.fetch_add(1, std::memory_order_relaxed);
return get_count();
}
long reduce_count() {
count_.fetch_sub(1, std::memory_order_relaxed);
return get_count();
}
long get_count() const { return count_.load(std::memory_order_release); }
private:
std::atomic_long count_;
};
~~~
- 空白目录
- 算法
- 排序
- 冒泡排序
- 选择排序
- 插入排序
- 归并排序
- 快速排序
- 计数排序
- 桶排序
- 基数排序
- 希尔排序
- 堆排序
- 二分查找
- 最小堆
- 最小索引堆
- 平衡二叉树(AVL tree)
- bitmap位图
- 布隆过滤器
- hashmap
- topK
- 跳表
- LRU Cache
- kmp
- 最小堆和堆排序
- 最短路径
- C++
- 运行时类型判断RTTI
- C++反射
- 手动实现智能指针
- 序列化实现
- rpc实现
- std::forward
- 函数指针的妙用
- C/C++
- std::function
- 同步队列
- 线程池实现
- std::promise
- 深入理解虚函数
- extern "C" 关键字讲解
- 大端小端的区别
- 简历
- 简历1
- redis
- 数据结构和对象
- sds
- list
- zskiplist
- 腾讯云redis面试题总结
- redis集群部署
- LeetCode
- 目标
- go基础
- 算法快速入门
- 数据结构篇
- 二叉树
- 链表
- 栈和队列
- 二进制
- 基础算法篇
- 二分搜索
- 排序算法
- 动态规划
- 算法思维
- 递归思维
- 滑动窗口思想
- 二叉搜索树
- 回溯法
- 其他
- 剑指offer
- 笔记
- git代理加速
- Linux
- vim大法
- vscode远程不能跳转
- cmake
- 设计模式
- 单例模式
- 简单工厂模式
- 外观模式
- 适配器模式
- 工厂方法模式
- 抽象工厂模式
- 生成器模式
- 原型模式
- 中介者模式
- 观察者模式
- 访问者模式
- 命令模式
- 网络编程
- epoll reactor模式
- linux timerfd系列函数总结
- IO
- mapreduce
- 反射器
- leo通信库
- Mutex
- Condition
- thread
- raft
- 协程
- hook
- 定时器
- 别人的面试经验
- 面试题
- vector崩溃问题
- JAVA
- Linux java环境配置
- ucore
- lab1
- FreeNOS
- leveldb
- 刷题笔记
- 回文串
- 前缀树
- 字符串查找
- 查找两个字符串a,b中的最长公共子串
- 动态规划
- golang
- 顺序循环打印实现
- 数据结构
- rpc运用
- python
- 单例
- 深拷贝浅拷贝
- 链表
- python基础题
- mysql
- 事务
- Linux
- 共享内存
- 刷题记录
- 贪心算法
- 动态规划
- 面试
- 腾讯C++面试
- 微众面试JD
- 迅雷网络面试
- 学习网址
- rabbitMq
