![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a3afa1556f282b002b98f46a1735a1af.gif#pic_center)
📄前言
在C++编程中,因为各种栈溢出、堆内存泄露、野指针等问题,C++程序员对内存的管理可谓是家常便饭,但即使如此,我们也会犯错,然后被那一大堆难以跟踪的错误打个猝不及防。于是乎,C++11引入了智能指针,其能够自动管理指针的生命周期,不再需要我们手动释放,简化了内存管理的复杂性。
🌺智能指针
概念
智能指针是C++为了实现RAII(Resource Acquisition Is Initialization)模式——利用对象生命周期来控制程序资源的技术而设计的。
智能指针比起传统的指针拥有更多的功能,智能指针将传统指针和各种自动化管理函数封装在一个类当中,从而实现RAII。
智能指针的类型
c++11标准库提供了三种智能指针,它们为:"unique_ptr "、“shared_ptr”、“weak_ptr”,在 C++98 也有着智能指针 auto_ptr(已废弃),但因为其存在极其明显的设计缺陷,所以在C++11中已经被废弃 。
-
auto_ptr :是C++标准库引入的第一个指针,实现了 RAII 的基础功能,但其设计不能允许同时被多个 auto_ptr 引用,而是把对指针所有权直接转移到另一个指针,导致指针被悬空。一个为了安全而制作的类,变成了另一个危险,所以它在C++11被废弃。
-
unique_ptr :是用于代替 auto_pt r的指针,其原理就是独占指针、防止被拷贝,利用类的生命周期来销毁指针。
-
shared_ptr:比起 unique_ptr,shared_ptr 可以被多个 shared_ptr 对象引用,通过引用计数来管理指针,只有对象所有的实例都被销毁时才将指针销毁。shared_ptr 的引用计数机制是线程安全的,但指向的资源不是。
-
weak_ptr:是一种没有管理权的智能指针,可用于”观察“对象,而不对它的生命周期进行管理。除此之外,它最重要的用途就是解决 shared_ptr 的循环访问问题(下面的代码将会展示)。
智能指针的使用
- auto_ptr 的使用:
void func()
{
auto_ptr<int> ptr1(new int);
auto_ptr<int> ptr2(ptr1); //指针被转移到了ptr2
*ptr2 = 10;
cout << *ptr2 << endl;
cout << *ptr1 << endl; // ptr1悬空,成为空指针
//出现异常。
}
- unique_ptr 的使用:
void func()
{
int* arr = new int;
unique_ptr<int> ptr(arr);
*ptr = INT_MAX; //像指针一样访问数据。
// unique_ptr<int> ptr2(ptr); unique_ptr 禁止指针被拷贝
//离开作用域自动销毁
}
- shared_ptr 的使用:
struct Arrary
{
int n;
shared_ptr<int> arr;
Arrary() { cout << "Arrary Create" << endl; }
~Arrary() { cout << "Arrary delete" << endl; }
};
void func()
{
shared_ptr<Arrary> ptr;
{
shared_ptr<Arrary> vec(new Arrary);
shared_ptr<Arrary> vec2 = make_shared<Arrary>();
vec->arr = make_shared<int>(50);
ptr = vec;
}
cout << "-------------------" << endl;
} //函数结束时,所有的指针被销毁
// 结果
// Arrary Create
// Arrary Create
// Arrary delete
// -------------------
// Arrary delete
- shared_ptr 的循环引用 :
// shared_ptr 的循环引用
class B;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
shared_ptr<B> ptr;
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
shared_ptr<A> ptr;
};
void function()
{
{
//make_shared 能提高创建内存的效率
auto a = make_shared<A>(); // A的计数++
auto b = make_shared<B>(); // B的计算++
a->ptr = b; // A++
b->ptr = a; // B++
//此时 A B 指针的引用都为2;
}
// a b 实例被销毁, A-- , B--;
// 但a 与 b 类内的ptr还指向着他们,等待着对方销毁。
// 他们的引用计数永远都不会降到零,导致内存泄漏
cout << "-----------------" << endl;
}
// 结果
// A()
// B()
// -----------------
// cout: 1 cout: 1
// +-----------+ shared_ptr +-----------+
// | A | ----------------> | B |
// |-----------| |-----------|
// | ptr | | ptr |
// +-----------+ +-----------+
- weak_ptr 的使用:
// weak_ptr 是用于打破循环引用而制作出来的产物
// weak_ptr 不会增加shared_ptr的引用计数
class B;
class A
{
public:
A() { cout << "A()" << endl; }
~A() { cout << "~A()" << endl; }
weak_ptr<B> ptr; // 更换为weak_ptr
};
class B
{
public:
B() { cout << "B()" << endl; }
~B() { cout << "~B()" << endl; }
weak_ptr<A> ptr;
};
void function()
{
{
auto a = make_shared<A>();
auto b = make_shared<B>();
a->ptr = b;
b->ptr = a;
}
cout << "-----------------" << endl;
}
//结果
// A()
// B()
// ~B()
// ~A()
// -----------------
🌻智能指针的简易实现
auto_ptr 的实现
auto _ptr 尽管已经被废弃,并且许多公司也禁止使用它,但自己动手来实现一下它,也能帮助你了解 unique_ptr 相对于它的改进。
template <class T>
class auto_ptr
{
private:
T* _ptr;
public:
explicit auto_ptr(T* ptr = nullptr)
:_ptr(ptr)
{} //使用explicit防止其被隐式转换
~auto_ptr()
{
if(_ptr)
delete _ptr;
}
auto_ptr(auto_ptr<T>& sp)
:_ptr(sp._ptr)
{
sp._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& sp)
{
if(this == &sp) return *this;
if(_ptr)
delete _ptr;
_ptr = sp._ptr;
sp._ptr = nullptr;
return *this;
}
// 像指针一样使用
T& operator*() const { return *_ptr; }
T* operator->() const { return _ptr; }
};
unique_ptr 的实现
unique_ptr 是对auto_ptr的改进,所以和auto_ptr有不少相识之处,unique_ptr 禁止自己管理的资源(指针)被转移。
template<class T>
class unique_ptr
{
public:
explicit unique_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if(_ptr)
delete _ptr;
}
unique_ptr(const unique_ptr<T>& ) = delete; //直接删除拷贝函数与赋值函数,防止资源被转移
unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
private:
T* _ptr;
};
shared_ptr 的使用
shared_ptr 使用计数机制来管理指针的生命周期,当计数为0时销毁指针,并且保证了计数功能的线程安全。
template <class T>
class shared_ptr
{
public:
explicit shared_ptr(T* p = nullptr)
:_ptr(p), _count(new int(1)), _mtx(new std::mutex)
{
std::cout << "shared_ptr(T*), count=" << *_count << std::endl;
}
shared_ptr(const shared_ptr& sp)
:_ptr(sp._ptr), _count(sp._count), _mtx(sp._mtx)
{
AddRef();
std::cout << "shared_ptr(shared_ptr&), count=" << *_count << std::endl;
}
void AddRef()
{
std::lock_guard<std::mutex> lock(*_mtx);
++(*_count);
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if(_ptr != sp._ptr)
{
release();
_ptr = sp._ptr;
_count = sp._count;
_mtx = sp._mtx;
AddRef();
}
std::cout << "operator=()" << std::endl;
return *this;
}
~shared_ptr()
{
release();
}
T* get() const
{
return _ptr;
}
int count() const { return *_count; }
void release()
{
bool flag = false;
{ //使用{}来限定作用域
std::lock_guard<std::mutex> lock(*_mtx); //锁住线程,并利用RAII机制自动销毁锁
if(--(*_count) == 0 && _ptr)
{
delete _ptr;
delete _count;
flag = true;
}
} //自动销毁锁
std::cout << "relese()" << std::endl;
if(flag) delete _mtx; //释放锁的资源
}
T& operator*() const { return *_ptr; }
T* operator->() const { return _ptr; }
private:
int* _count; //计数指针
std::mutex* _mtx; //线程锁
T* _ptr; // 指针
};
📓总结
智能指针是为了实现RAII而设计的产物,智能指针包括 “unique_ptr”、 “shared_ptr”、 “weak_ptr”,它们提供了一种安全、高效的方式来管理内存,减少了程序出错的概率,也许你在内存中看不到自动化管理的重要性,但总有一天,你会接触到多线程的管理,此时RAII便会烙入你的心中。
![](https://i-blog.csdnimg.cn/blog_migrate/6978e059d51da817bf235e35f931e580.gif#pic_center)