前言
在第一部分内容中,可以看到如何管理资源。C++祭出的方法是RAII,用资源类来进行管理。但是如何实现这个资源类,却又摆在了面前。在C++原生的库里面支持智能指针auto_ptr
,但是这个类在C++17被删除了,取而代之的是shared_ptr
和unique_ptr
这两个从名字上就可以看出来,一个是和原来auto_ptr
类似的不存在共享的情况,另一个是允许共享的情况。
既然我们要自己实现一个智能指针,那么应该了解智能指针有些什么功能,也可以称为有什么接口。
这个表格是来自于《C++ primer》,在这里面可以看到需要实现的函数。
- 空白构造函数
- 布尔表达式
- *操作符
- ->操作符
- get()函数
- swap()函数
- 拷贝构造函数
- =操作符
- unique()函数
- use_count()函数
unique_ptr
对于unique_ptr来说,不允许出现拷贝构造函数和赋值运算符。所以对于这两个函数需要进行禁止。
smart_ptr(const smart_ptr& other) = delete;
smart_ptr(const smart_ptr& rhs) = delete;
下面的介绍主要以shared_ptr 作为核心。
实现模板化
template <typename T>
class smart_ptr {
public:
explicit smart_ptr(T* ptr = nullptr) : ptr_(ptr) {}
~smart_ptr() { delete ptr_; }
T* get() const { return ptr_; }
private:
T* ptr_;
};
下面的内容我们就在这个基础框架上进行增加内容了。为了实现布尔表达式,*操作符和->操作符,就需要对这部分操作符进行重载操作。
重载操作符
operator bool() { return ptr_; }
T& operator*() { return *ptr_; }
T* operator->() { return ptr_; }
重载这三个就可以实现*point
,point->x
,进行布尔操作
测试函数:
int main() {
int x = 5;
shared_wrapper<int> point(&x);
cout << "*point : " << *point << endl;
cout << "point-> : " << point.operator->() << endl;
cout << "point bool : " << (point ? (1) : (2)) << endl;
}
实验结果:
swap函数
C++在STL库中自带了swap函数,但是我们要命名同名函数就会把标准库函数的STL的作用域覆盖掉,所以就需要使用using这一个关键字将swap作用域重新提出来。
void swap(smart_ptr& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
}
既然前面可以对这个指针进行操作,所以我们也可以实现。对于两个ptr
类的对象进行赋值和拷贝操作。
smart_ptr(smart_ptr& other) {
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr& rhs) {
rhs.swap(*this);
return *this;
}
大家可能会疑问这个release()
是个什么作用的。其实他的作用就是和unique_ptr()
相同的功能,释放当前的指针的所有权。这样就把所有权通过返回值的方式释放出去了。
T* release() {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
既然可以将所有权释放出去,那么就会涉及到拷贝构造函数和移动构造函数。
smart_ptr(smart_ptr&& other) {//移动构造函数
ptr_ = other.release();
}
smart_ptr(smart_ptr& other) {//拷贝构造函数
rhs.swap(*this);
return *this;
}
可以看一下两者的差别,就多了一个&
符号而已。那么我们来测试一下。
int main() {
int x = 4;
shared_wrapper<int> point_1(&x);
shared_wrapper<int> point_2(point_1);
cout << "point_1 : " << *point_1 << endl;
shared_wrapper<int> point_3(move(point_1));
cout << "point_1 : " << *point_1 << endl;
}
测试结果
为什么没有输出最后一行的*point_1呢?,因为通过了移动构造函数过后,当前位置的元素地址已经由另一个指针来进行管理,而原来的指针已经复位为nullptr
了。
引用计数
shared_ptr里面对于指向同一区域的指针个数进行了计数,所以我们需要定义一个类来存储计数值。
class count {
public:
void add_count() { count_++; }
long reduce_count() { return --count_; }
long use_count() { return count_; }
count() : count_(1){}
private:
long count_;
};
这样就需要修改之前的类定义函数了。
template <typename T>
class shared_wrapper {
public:
template<typename U>
friend class shared_wrapper;
explicit shared_wrapper(T* ptr = nullptr) : ptr_(ptr) {
if (ptr)
{
ptr_count = new shared_count();
}
}
~shared_wrapper(){
if (ptr_ && !ptr_count->reduce_count())
{
delete ptr_count;
delete ptr_;
}
}
T* get() { return ptr_; }
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
template <typename U>
shared_wrapper(shared_wrapper<U>& other ) {
ptr_ = other.ptr_;
if (ptr_)
{
other.ptr_count->add_count();
ptr_count = other.ptr_count;
}
}
template <typename U>
shared_wrapper(shared_wrapper<U>&& other ) {
ptr_ = other.ptr_;
if (ptr_)
{
ptr_count = other.ptr_count;
other.ptr_ = nullptr;
}
}
shared_wrapper& operator= (shared_wrapper rhs) {
rhs.swap(*this);
return *this;
};
T* release() {
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(shared_wrapper& rhs) {
using std::swap;
swap(ptr_, rhs.ptr_);
swap(ptr_count, rhs.ptr_count);
}
long use_count() const {
if (ptr_)
{
return ptr_count->get_count();
}
else
{
return 0;
}
}
private:
T* ptr_;
shared_count *ptr_count;
};
测试函数:
int main() {
int x = 5;
shared_wrapper<int> point(&x);
cout << *point.get() << endl;//5
cout << *point << endl;//5
cout << point.use_count() << endl;//1
shared_wrapper<int> point_2(point);
cout << point.use_count() << endl;//2
shared_wrapper<int> point_3(point);
cout << point.use_count() << endl;//3
shared_wrapper<int> point_4(move(point_2));
cout << point.use_count() << endl;//3
}
运行结果: