文章参考:
所谓智能指针,其实就是C++11封装的类,里面存有一个正常指针,智能指针会通过这个正常指针,来监视指针指向的内存,当没有智能指针指向该内存时,该内存就被释放。其核心在于引用计数
,每一个智能指针指向内存A,智能指针内部的引用计数就加一。每析构一次,就减一。当引用计数为0时,删除指向的堆内存。
C++11提供三种智能指针,头文件为<memory>
:
std::shared_ptr
:共享的智能指针。std::unique_ptr
:独占的智能指针。std::weak_ptr
:若引用的智能指针,它不共享指针,不能操作资源,而是用来监视shared_ptr
的。
1. shared_ptr的初始化
共享智能指针是指可以有多个智能指针
(普通指针不计数)同时管理同一块有效的内存。shared_ptr
是一个模板类,有三种初始化方法:
- 通过构造函数
std::make_shared
辅助函数reset
方法
如果想要查看有多少智能指针同时管理者某一块内存,可以使用共享智能指针的成员函数use_count
,原型如下:
long use_count() const noexcept;
1.1 通过构造函数初始化
#include <iostream>
#include <memory>
using namespace std;
int main(void){
shared_ptr<int> ptr1(new int(1));
cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
shared_ptr<char> ptr2(new char[3]);
cout << "ptr2.use_cout()==" << ptr2.use_count() << endl;
shared_ptr<char[]> ptr3(new char[3]);
cout << "ptr3.use_cout()==" << ptr3.use_count() << endl;
shared_ptr<int> ptr4;
cout << "ptr4.use_cout()==" << ptr4.use_count() << endl;
shared_ptr<int> ptr5(nullptr);
cout << "ptr5.use_cout()==" << ptr5.use_count() << endl;
return 0;
}
输出:
ptr1.use_cout()==1
ptr2.use_cout()==1
ptr3.use_cout()==1
ptr4.use_cout()==0
ptr5.use_cout()==0
结论:
- 如果智能指针被初始化了一块有效内存,那么该内存的引用计数+1,如果智能指针没有被初始化或者被初始化为
nullptr
空指针,引用计数不会+1。 - 不要用一个原始指针初始化多个共享指针,这回导致原始指针的引用计数无法+1。
1.2 通过拷贝构造和移动构造初始化
#include <iostream>
#include <memory>
using namespace std;
int main(void){
shared_ptr<int> ptr1(new int(1));
cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
shared_ptr<int> ptr2(ptr1); // 拷贝构造
cout << "ptr2.use_cout()==" << ptr2.use_count() << endl;
shared_ptr<int> ptr3(move(ptr1)); // 移动构造
cout << "ptr1.use_cout()==" << ptr1.use_count() << endl;
cout << "ptr3.use_cout()==" << ptr3.use_count() << endl;
return 0;
}
输出:
ptr1.use_cout()==1
ptr2.use_cout()==2
ptr1.use_cout()==0
ptr3.use_cout()==2
分析:
- 第9行:通过拷贝构造初始化共享智能指针,
ptr1
和ptr2
的引用计数都+1。 - 第10行:通过移动构造初始化共享智能指针,此时
ptr1
的管理的内存被转移给ptr3
管理,所以ptr1
的引用计数归0,而ptr3
的引用计数和ptr1
原本的引用计数一样。
1.3 通过std::make_shared初始
C++11提供std::make_shared
方法,用于完成内存对象的创建,并将创建好的内存对象初始化给智能指针。函数原型如下:
template <class T, class ... Args>
shared_ptr<T> make_shared(Args&&.... args);
EG:
-
代码:
#include <iostream> #include <memory> using namespace std; class Test { private: int num; public: Test() { cout << "non-parameter constructor" << endl; } Test(int n): num(n) { cout << "parameterized constructor" << endl; } ~Test() { cout << "destructor" << endl; } void setValue(int n) { num = n; } void print(){ cout << "Test.num==" << num << endl; } }; int main(void){ shared_ptr<int> ptr1 = make_shared<int>(100); cout << "ptr1.use_count()==" << ptr1.use_count() << endl; shared_ptr<Test> ptr2 = make_shared<Test>(); cout << "ptr2.use_count()==" << ptr2.use_count() << endl; shared_ptr<Test> ptr3 = make_shared<Test>(100); cout << "ptr3.use_count()==" << ptr3.use_count() << endl; return 0; }
-
输出:
ptr1.use_count()==1 non-parameter constructor ptr2.use_count()==1 parameterized constructor ptr3.use_count()==1 destructor destructor
-
分析:
使用
std::make_shared()
模板函数可以完成内存地址的创建,并将最终得到的内存地址传递给共享智能指针对象管理。且在申请内存的同时,可以调用对应的构造参数对对象进行初始化。
1.5 使用reset
方法进行初始化
共享智能指针提供成员函数reset来对自身进行初始化。
原型:
void reset() noexcept;
template <class Y>
void reset(Y* ptr);
template <class Y, class Deleter>
void reset(Y* ptr, Deleter d);
template <class Y, class Deleter, class Alloc>
void reset(Y* ptr, Deleter d, Alloc alloc);
其中:
- ptr:指向要获取所有权的对象的指针。
- d:删除器,指向要获取所有权的对象的指针。
- alloc:内存存储所用的分配器。
作用:
有两种作用:
-
放弃当前共享智能指针对内存的指向。
ptr1.reset();
-
转移共享智能指针的指向:
shared_ptr<int> ptr1(new int(100)); ptr1.reset(new int(200)); //
EG:
-
代码:
#include <iostream> #include <memory> using namespace std; int main(void){ shared_ptr<int> ptr1 = make_shared<int>(100); cout << "ptr1.use_count()==" << ptr1.use_count() << endl; shared_ptr<int> ptr2 = ptr1; cout << "ptr2.use_count()==" << ptr2.use_count() << endl; shared_ptr<int> ptr3 = ptr1; cout << "ptr3.use_count()==" << ptr3.use_count() << endl; cout << endl; ptr3.reset(); cout << "ptr1.use_count()==" << ptr1.use_count() << endl; cout << "ptr2.use_count()==" << ptr2.use_count() << endl; cout << "ptr3.use_count()==" << ptr3.use_count() << endl; cout << endl; ptr2.reset(new int(200)); cout << "ptr1.use_count()==" << ptr1.use_count() << endl; cout << "ptr2.use_count()==" << ptr2.use_count() << endl; cout << "ptr3.use_count()==" << ptr3.use_count() << endl; return 0; }
-
输出:
ptr1.use_count()==1 ptr2.use_count()==2 ptr3.use_count()==3 ptr1.use_count()==2 ptr2.use_count()==2 ptr3.use_count()==0 ptr1.use_count()==1 ptr2.use_count()==1 ptr3.use_count()==0
1.5 操作内存
在使用共享智能指针时,如果想要操作指向的目标,有两种方式:
-
通过成员函数
get()
方法获取原始指针,用原始指针来操作。get
的函数原型如下:T* get() const noexcept;
-
把共享智能指针当作原始指针,直接操作。(实际上是在共享智能指针类内部进行了操作符重载)
EG:
-
代码:
#include <iostream> #include <memory> using namespace std; class Test { private: int num; public: Test() { cout << "non-parameter constructor" << endl; } Test(int n): num(n) { cout << "parameterized constructor" << endl; } ~Test() { cout << "destructor" << endl; } void setValue(int n) { num = n; } void print(){ cout << "Test.num==" << num << endl; } }; int main(void) { shared_ptr<Test> ptr1 = make_shared<Test>(100); Test* p = ptr1.get(); p.print(); ptr1->setValue(200); ptr1->print(); return 0; }
-
输出:
Test.num==100 Test.num==200
2. 指定删除器
2.1 自定义删除器
删除器:
当智能指针管理的内存对应的引用计数为0时,这块内存会被智能指针析构掉。我们可以在初始化智能指针时自己指定删除动作,也就是删除器
,其本质上是一个函数。我们只需要实现该函数,器调用由智能指针完成。
EG:
-
代码:
#include <iostream> #include <memory> using namespace std; void deletor(int *p){ cout << "custom deletor" << endl; delete p; } int main(void) { shared_ptr<Test> ptr1(new int(100), deletor); return 0; }
-
输出:
custom deletor
2.2 lambda 删除器
可以使用lambda表达式作为删除器函数:
#include <iostream>
#include <memory>
using namespace std;
int main(void) {
shared_ptr<char> ptr(new char('a'),
[](char *p){
cout << "lambda deletor" << endl;
delete p;
})
return 0;
}
输出:
lambda deletor
2.3 数组删除器
在较早版本的C++11中,std::shared_ptr的默认删除器不支持数组对象
,如:
class Test {
public:
Test() { cout << "constructor" << endl; }
~Test() { cout << "destructor" << endl; }
};
shared_ptr<Test> ptr(new Test[5]);
这会导致报错:
constructor
constructor
constructor
constructor
constructor
destructor
段错误 (核心已转储)
明明有五个对象被创建了,却只析构了一个对象,因此内存泄漏,出现段错误。
面对这种问题,有两种解法:
-
在声明智能指针模板类型时,使用
数组
。class Test { public: Test() { cout << "constructor" << endl; } ~Test() { cout << "destructor" << endl; } }; shared_ptr<Test[]> ptr(new Test[5]); // 这里将模板参数声明为数组类型
-
定义数组删除器。又有两种方式:
-
自定义删除器:
class Test { public: Test() { cout << "constructor" << endl; } ~Test() { cout << "destructor" << endl; } }; shared_ptr<Test> ptr(new Test[5], [](Test* p){ delete[] p; });
-
使用C++提供的
std::default_delete<T>()
函数作为删除器。该函数内部逻辑也是通过delete
来实现的,要释放什么类型的类型,就指定什么类型的模板参数T
。class Test { public: Test() { cout << "constructor" << endl; } ~Test() { cout << "destructor" << endl; } }; shared_ptr<Test> ptr(new Test[5], default_delete<Test[]>());
-