简介
智能指针就是对指针进行封装,使其提供特有的功能。
unique_ptr:封装了原始指针使其只能在同一时刻被同一对象拥有,并且在离开作用域时会自动销毁。
shared_ptr: 封装了原始指针,利用引用技术技术,实现多个对象同时共享一个指针,并且在所有对象都离开作用域时释放内存.
weak_ptr : 用来解决shared_ptr带来的循环计数问题,而且weak_ptr中的lock函数保证是线程安全
实现一个简单的unique_ptr
unique_ptr主要功能 :
- 不能够赋值,拷贝,允许移动
- operator* 实现
- operator→ 实现
- default_delete实现
- reset, release, get实现
template <class Tp, class Dp = default_delete> class unique_ptr { public: typedef Tp element_type; typedef Delete delete_type; typedef element_type* pointer; typedef element_type& reference; public: unique_ptr(pointer data = nullptr) : data_(data) {} unique_ptr(pointer data, delete_type del) : data_(data), del_(del) {} ~unique_ptr() { clear(); } // 不允许赋值,拷贝 unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; // 允许移动 unique_ptr(unique_ptr&& up) : data_(up.relase()), del_(up.del_) {} unique_ptr& operator=(unique_ptr&& up) { if (&up != *this) { reset(up.release()); del_ = up.del_; } return *this; } private: Tp* data_; Dp del_; };
上面的代码实现了第一步,不允许赋值拷贝,允许移动,代码非常简单,unqiue_ptr有两个模板参数,第一个是指针类型,第二个参数就是对应着这个指针的删除器
reset, release和clear实现 :
pointer release() noexcept{
auto res = data_;
data_ = nullptr;
return res;
}
void reset(pointer p = pointer()) noexcept {
auto tmp = data_;
data_ = p;
if (tmp)
del_(tmp);
}
void clear() const {
del_(data_);
data_ = nullptr;
}
elease函数的作用就是放弃原生指针的所有权,reset的功能是用一个新指针来替换原来的指针
operator*, operator→实现:
reference operator*() const { return (*data_); }
pointer operator->() const noexcept { return data_; }
defalut_delete实现 :
template
struct default_delete {
void operator()(T* ptr) {
if (ptr)
delete ptr;
}
};
template
struct default_delete {
void operator()(T* ptr) {
if (ptr)
delete []ptr;
}
}
这两个default_delete利用模板偏特化技术分别实现了删除普通指针以及数组指针, 到这里unique_ptr就已经实现完了,代码并不复杂,很简单,最后来看一个make_unique
tempalte
unique_ptr make_unique(Args&&... args)
{
return unique_ptr(new T(std::forward(args)...));
}
- 引用计数
- operator*
- operator→
- 赋值拷贝构造函数
引用计数实现:
template
class RefCount {
friend ClassName;
RefCount(T* p) : pointer_(p), count(1) {}
~RefCount() { delete pointer_; }
T* pointer_;
size_t count_;
};
这个引用计数实现的非常简单,仅仅包含了一个指针和一个计数值,真正的操作放到了Shared_ptr里面了
operator*, operator→, 赋值,拷贝构造函数实现:
template
class shared_ptr {
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
public:
shared_ptr(value_type* ptr) : ptr_(new RefCount(ptr)) {}
shared_ptr(const shared_ptr& sp) : ptr_(sp) {
++ptr_->count_;
}
shared_ptr& operator=(const shared_ptr& rhs) {
if (&rhs == this) {
// 由于rhs需要赋值给*this,所以将rhs的计数先+1
++rhs.ptr_->count_;
// 由于*this 需要抛弃掉本身保存的指针,所以将计数-1并判断是否已经是最后一个
if (--ptr_->count_ == 0)
delete ptr_;
ptr_ = rhs.ptr_;
}
retur *this;
}
~shared_ptr() {
if (--ptr_->count_ == 0)
delete ptr_;
}
reference operator*() const { return *ptr_->pointer_;}
pointer operator->() const { return &(operator*()); }
private:
RefCount* ptr_;
};
shared_ptr中最主要的就是拷贝构造和赋值运算符以及析构函数中对count进行的管理,在析构的时候需要将count减1,并判断是否为0,为0就表示当前是最后一个引用这个指针的shared_ptr,需要释放资源,在赋值和拷贝时都需要将计数+1, 在来看一下make_shared.
template
shared_ptr make_shared(Args&&... args> {
return shared_ptr(new T(std::forward(args)...));
}
由上面我们实现的代码可以看出,在对count进行操作时不是多线程安全的,在标准库的实现中引用计数是线程安全的,它在底层用的是原子操作,也就是说在多线程情况下它只会释放一次也时线程不安全的,但是在构造,swap,reset操作中不是线程安全的,所以在多线程中共享shared_ptr需要格外的小心,要么加锁来保证安全,或者用weak_ptr来代替shared_ptr
#include#include using namespace std; class B; class A { public:// 为了省去一些步骤这里 数据成员也声明为public shared_ptr pb; void doSomthing() { } ~A() { cout << "kill A\n"; } }; class B { public: shared_ptr pa; ~B() { cout <<"kill B\n"; } }; int main(int argc, char** argv) { shared_ptr sa(new A()); shared_ptr sb(new B()); if(sa && sb) { sa->pb=sb; sb->pa=sa; } cout<<"sa use count:"< endl; return 0; } ()
上面的代码运行结果为:sa use count:2, 注意此时sa,sb都没有释放,产生了内存泄露问题!!
为什么产生内存泄漏 : 由于A里面有一个B的shared_ptr, 所以在A析构之前B必须析构,但是是B里面又有一个A的shared_ptr, 所以在B析构之前,A必须析构,有木有发现逻辑全乱了,就是这样就导致了循环引用,也就内存泄漏了.
#include#include using namespace std; class B; class A { public:// 为了省去一些步骤这里 数据成员也声明为public weak_ptr pb; void doSomthing() { shared_ptr pp = pb.lock(); if(pp)//通过lock()方法来判断它所管理的资源是否被释放 { cout<<"sb use count:"< endl; } } ~A() { cout << "kill A\n"; } }; class B { public: shared_ptr pa; ~B() { cout <<"kill B\n"; } }; int main(int argc, char** argv) { shared_ptr sa(new A()); shared_ptr sb(new B()); if(sa && sb) { sa->pb=sb; sb->pa=sa; } sa->doSomthing(); cout<<"sb use count:"< ()endl; return 0; } ()
Original: https://www.cnblogs.com/gd-luojialin/p/15028157.html
Author: gd_沐辰
Title: C++智能指针原理
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/535556/
转载文章受原作者版权保护。转载请注明原作者出处!