目录
在C++中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。
C++11中提供了三种智能指针,使用这些智能指针时需要引用头文件
std::shared_ptr:共享的智能指针
std::unique_ptr:独占的智能指针
std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。
shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。
1. shared_ptr的初始化
共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针shared_ptr 是一个模板类,如果要进行初始化有三种方式:
通过构造函数;
std::make_shared辅助函数;
reset方法。
共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数use_count
2.获取原始指针
对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的get()方法得到原始地址
3. 指定删除器
当智能指针管理的内存对应的引用计数变为0的时候,这块内存就会被智能指针析构掉了。另外,我们在初始化智能指针的时候也可以自己指定删除动作,这个删除操作对应的函数被称之为删除器,这个删除器函数本质是一个回调函数,我们只需要进行实现,其调用是由智能指针完成的。
4. 注意事项
使用
#includeusing namespace std; #include #include class Test { public: Test() : m_num(0) { cout << "construct Test..." << endl; } Test(int x) : m_num(0) { cout << "construct Test, x = " << x << endl; } Test(string str) : m_num(0) { cout << "construct Test, str = " << str << endl; } ~Test() { cout << "destruct Test..." << endl; } void setValue(int v) { this->m_num = v; } void print() { cout << "m_num: " << this->m_num << endl; } private: int m_num; }; int main() { /*-------------------------- 一,初始化智能指针shared_ptr ------------------------------*/ //1.通过构造函数初始化 shared_ptr ptr1(new int(3)); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; //2.通过移动和拷贝构造函数初始化 shared_ptr ptr2 = move(ptr1); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; shared_ptr ptr3 = ptr2; cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; //3.通过 std::make_shared初始化 shared_ptr ptr4 = make_shared (8); shared_ptr ptr5 = make_shared (7); shared_ptr ptr6 = make_shared ("GOOD LUCKLY!"); //4.通过reset初始化 ptr6.reset(); //重置ptr6, ptr6的引用基数为0 cout << "ptr6管理的内存引用计数: " << ptr6.use_count() << endl; ptr5.reset(new Test("hello")); cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl; cout << endl; cout << endl; /*-------------------------- 二,共享智能指针shared_ptr的使用 ------------------------------*/ //1.方法一 Test* t = ptr5.get(); t->setValue(1000); t->print(); //2.方法二 ptr5->setValue(7777); ptr5->print(); printf("\n\n"); /*------------------------------------ 三,指定删除器 -----------------------------------*/ //1.简单举例 shared_ptr ppp(new Test(100), [](Test* t) { //释放内存 cout << "Test对象的内存被释放了......." << endl; delete t; }); printf("----------------------------------------------------------------------\n"); 2.如果是数组类型的地址,就需要自己写指定删除器,否则内存无法全部释放 //shared_ptr p1(new Test[5], [](Test* t) { // delete[]t; // }); //3.也可以使用c++给我们提供的 默认删除器函数(函数模板) shared_ptr p2(new Test[3], default_delete ()); //4.c++11以后可以这样写 也可以自动释放内存 shared_ptr p3(new Test[3]); return 0; }
数组模板
#include#include #include using namespace std; //有了这个函数模板,我们就不用自己去释放数组类型的地址了 template shared_ptr make_share_array(size_t size) { //返回匿名对象 return shared_ptr (new T[size], default_delete ()); } int main() { shared_ptr ptr1 = make_share_array (10); cout << ptr1.use_count() << endl; shared_ptr ptr2 = make_share_array (7); cout << ptr2.use_count() << endl; return 0; }
std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝。
从哲学的角度讲,unique 与share 实现内存安全的思路完全相反,一个是通过不允许多份内存指针,一个是对内存指针的使用进行计数。
1. 初始化
std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。
2. 删除器
unique_ptr指定删除器和shared_ptr指定删除器是有区别的,unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器
using namespace std;
struct A {
~A() {
cout << "A delete" << endl;
}
void Print() {
cout << "A" << endl;
}
};
int main() {
auto ptr = std::unique_ptr(new A);
auto tptr = std::make_unique(); // error, c++11还不行,需要c++14
std::unique_ptr tem = ptr; // error, unique_ptr不允许移动
ptr->Print();
return 0;
}
weak_ptr的思路就是观察者模式,是用来监视shared_ptr的生命周期,std::weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视shared_ptr中管理的资源是否存在,可以用来返回this指针和解决循环引用问题。
1. 初始化
#include#include using namespace std; int main() { shared_ptr sp(new int); weak_ptr wp1; weak_ptr wp2(wp1); weak_ptr wp3(sp); weak_ptr wp4; wp4 = sp; weak_ptr wp5; wp5 = wp3; return 0; }
weak_ptr
weak_ptr
weak_ptr
wp4 = sp;通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象(这是一个隐式类型转换)
wp5 = wp3;通过一个weak_ptr对象构造了一个可用的weak_ptr实例对象
通过调用std::weak_ptr类提供的use_count()方法可以获得当前所观测资源的引用计数
2. 常用函数
通过调用std::weak_ptr类提供的expired()方法来判断观测的资源是否已经被释放
通过调用std::weak_ptr类提供的lock()方法来获取管理所监测资源的shared_ptr对象
通过调用std::weak_ptr类提供的reset()方法来清空对象,使其不监测任何资源
3. 解决了share_ptr 的哪些问题
struct A;
struct B;
struct A {
std::shared_ptr bptr;
~A() {
cout << "A delete" << endl;
}
void Print() {
cout << "A" << endl;
}
};
struct B {
std::weak_ptr aptr; // 这里改成weak_ptr
~B() {
cout << "B delete" << endl;
}
void PrintA() {
if (!aptr.expired()) { // 监视shared_ptr的生命周期
auto ptr = aptr.lock();
ptr->Print();
}
}
};
int main() {
auto aaptr = std::make_shared();
auto bbptr = std::make_shared();
aaptr->bptr = bbptr;
bbptr->aptr = aaptr;
bbptr->PrintA();
return 0;
}
输出:
A
A delete
B delete

_M_use_count是如何加减的
template__shared_ptr(const __shared_ptr<_Yp, _Lp>& __r, element_type* __p) noexcept : _M_ptr(__p), _M_refcount(__r._M_refcount) // never throws { } __shared_count(const __shared_count& __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != 0) _M_pi->_M_add_ref_copy(); } template <> inline void _Sp_counted_base<_S_single>::_M_add_ref_copy() { ++_M_use_count; } template _Assignable &> operator=( const shared_ptr<_Yp>& __r) noexcept { this->__shared_ptr<_Tp>::operator=(__r); return *this; } template _Assignable<_Yp> operator=(const __shared_ptr<_Yp, _Lp>& __r) noexcept { _M_ptr = __r._M_ptr; _M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw return *this; } __shared_count& operator=(const __shared_count& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != _M_pi) { if (__tmp != 0) __tmp->_M_add_ref_copy(); if (_M_pi != 0) _M_pi->_M_release(); _M_pi = __tmp; } return *this; } ~__shared_count() noexcept { if (_M_pi != nullptr) _M_pi->_M_release(); } template <> inline void _Sp_counted_base<_S_single>::_M_release() noexcept { if (--_M_use_count == 0) { _M_dispose(); if (--_M_weak_count == 0) _M_destroy(); } } virtual void _M_dispose() noexcept { delete _M_ptr; }