目录
Smart Pointer(智能指针)指的是一类指针,并不是单一某一个指针,它能知道自己被引用的个数以至于在最后一个引用消失时销毁它指向的对象,本文主要介绍C++2.0提供的新东西。
一、Smart Pointer分类
C++2.0提供了两大类型的智能指针,该模块都被定义于头文件<memory>:
- shared_ptr实现共享式拥有概念。多个smart pointer既共享也拥有对象,这个拥有的意思表面上可以理解为计数器的个数。在这里特别强调是为了区分后面讲到的weak_ptr。当最后一个引用的指针被销毁时,这个对象也被析构(这里也可以理解为当计数器的个数为0时,对象被析构)。
- unique_ptr实现独占式拥有概念。保证在同一时间内只有一个smart pointer可以指向该对象。可以将移交拥有权给其他smart pointer。
二、标准库提供的智能指针类
2.1 class shared_ptr
提供了共享式拥有语义,也就是说当单个shared_ptr可以共享(或拥有)同一个对象,对象的最后一个拥有者有责任销毁对象,并清理与该对象相关的所有资源,也就是说它所指向的对象不再被需要时,自动释放(当超出作用域时,其析构函数被调用,在析构函数中,将其引用计数减1,如果引用计数的值变为0,则删除关联的原始指针,默认使用delete释放内存)与对象相关的资源。
2.2 shared_ptr使用
可以像其他任何指针一样使用,可以赋值、拷贝、比较它们也可以使用*和->操作符访问所指向的内容。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
int main()
{
// two shared pointers representing two persons by their name
shared_ptr<string> pNico(new string("nico"));//shared_ptr<string> pNico = make_shared<string>("nico");
shared_ptr<string> pJutta(new string("jutta"));//注意,直接这种形式的构造表示这个构造函数是explicit
// capitalize person names
(*pNico)[0] = 'N';
pJutta->replace(0, 1, "J");
// put them multiple times in a container
vector<shared_ptr<string>> whoMadeCoffee;//此处放进去的是指针的引用,不是指针的内容
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
// print all elements
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
// overwrite a name again
*pNico = "Nicolai";
// print all elements again
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
// print some internal data
cout << "use_count: " << whoMadeCoffee[0].use_count() << endl;
//use_count 返回第一个元素被引用的次数,容器里面的三个拷贝和自身,加起来为4
}
运行结果:
2.3 shared_ptr自定义析构函数
再上图例子中,如果我们想自己定义析构函数的规则,不想用string*默认的,那我们就需要在构造时增加一点改动:
传递一个lambda作为shared_ptr构造函数的第二实参,这样申明的方式当其最末一个拥有者被摧毁时,会调用这个lambda
shared_ptr<string> pNico(new string("nico"), [](string* p) {
cout << "delete" << *p << endl;
delete p; });
2.4 shared_ptr对于数组的使用
一般我们使用shared_ptr的时候没有指定其析构函数,是因为shared_ptr有一个默认的析构函数,这个默认的析构函数调用的是delete函数,这就意味着shared_ptr拥有的是由new建立起来的单一对象时,default delete才能适用,但是当我们使用数组的时候(数组需要delete[]),这个默认的delete就不适用了,我们需要自己定义delete.
std::shared_ptr<int> p(new int[10], [](int *p) {delete[] p; });
也可使用编译器为unique_ptr提供的辅助函数作为deleter析构策略:
std::shared_ptr<int> p(new int[10], std::default_delete<int[]>());
注意:shared_ptr不提供operator [],只提供operator*和operator->,想访问内存,必须使用get()函数来获取被shared_ptr包裹的内部指针;
2.5 shared_ptr析构策略
当shared_ptr的最后一个声明周期结束后,如果清理工作不仅仅是删除内存,这时你必须明确给出自己的deleter,可以指定属于自己的析构策略。
下例展示:当指向临时文件的的最后一个引用消失时,我们要删除这个文件
#include <string>
#include <fstream> // for ofstream
#include <memory> // for shared_ptr
#include <cstdio> // for remove()
class FileDeleter
{
private:
std::string filename;
public:
FileDeleter (const std::string& fn)
: filename(fn) { std::cout << "constructor" << std::endl;
}
void operator () (std::ofstream* fp) { std::cout << "delete" << std::endl;
delete fp; // close file
std::remove(filename.c_str()); // delete file 删除文件
}
};
int main()
{
// create and open temporary file://这里会创建一个shared_ptr指针,令他指向new新建的输出文件,FileDeleter将负责shared_ptr的最后一个拷贝失去此输出文件的所有权时进行一系列清理操作23 std::shared_ptr<std::ofstream> fp(new std::ofstream("tmpfile.txt"),
FileDeleter("tmpfile.txt"));//解释上一行的运行原理:首先new时会产生一个临时对象,FileDeleter("tmpfile.txt")也会产生一个临时对象,当析构时,会将new的临时对象传给FileDeleter,即调用()运算符 std::cout << "reset" << std::endl; fp.reset(); std::cout << "reset end" << std::endl;25 //... 26
}
2.6 常见成员函数以及分类介绍
作者:灼光
出处:灼光 - 博客园
本博客文章大多为原创,转载请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。