1. 智能指针之shared_ptr
1.简介
shared_ptr( 共享内存指针),shared_ptr是一个共享内存的指针,多个指针可以共同使用同一个对象资源,也就是说多个shared_ptr可以同时“拥有”或者“共享”同一个对象,当最后一个地方使用之后会自动释放相应的内存与资源。shared_ptr与其他的智能指针一样都定义在了<memory>头文件中。
2. 定义
//这里的pStr是一个栈对象,当最后一个使用之后会自动清理内存与资源
shard_ptr<std::string>pStr(new string(“hello world”);
3. 重载的运算符
所有的智能指针都重载了”->”运算符以及“*”,这里我们需要注意“.”与“->”的区别其中”.”运算符是是调用的shared_ptr中的方法,而->则是调用指针对象内中的方法,“*”是对其指针进行解引用。
例:
#include <iostream>
#include <memory>
#include <string>
#include <iostream>
using namespace std;
int main()
{
shared_ptr<string >p1(new string("hello "));
p1->append("world");
string *p2 = p1.get();
cout << *p2;//输出为hello world
return 0;
}
其中p1.get();返回的是shared_ptr中管理的string*
_Ty *get() const _NOEXCEPT
{ // return pointer to resource
return (this->_Get());
}
注意:当使用(*p1)时返回的是 其中内容的引用,使用pStr->时能够访问其中内容的成员。
千万注意 “.” 和 “->” 的区别
4. 初始化shared_ptr
该类的构造函数被explicit修饰,所以以下便是错误的写法:
shared_ptr<string> pNico = new string("nico"); // ERROR
shared_ptr<string> pNico{new string("nico")}; // OK
当然标准库还提供了一个专门用于生成shared_ptr的函数,而且使用该方式既快又安全,因为如果使用make_shared只用申请一次内存块,而使用常规的new 操作会先申请内存,然后申请内存控制。当使用常规的new申请内存时,不会立即将裸指针的控制权交给shared_ptr,如果在此时程序抛出一个异常,则程序将会出现内存泄漏的问题。
当然使用make_shared也会缺点,那就是当配合使用weak_ptr时,因为只分配了一次内存,那么控制块的内存以及对象分配的内存则会在一起,当此时如果强引用的计数为0时,按理说此时应该释放分配的资源,但是此时如果存在weak_ptr的弱引用,那么将不会立即释放该资源。
例:
shared_ptr<string >p3 = std::make_shared<string>("abc");
当然shared_ptr 也可以更改其中改的指针内容,但是不能直接new。
例如:
std::shared_ptr<string>pStr(new string(“hello world”));
pStr = new string (“hehehe”);//error
//正确的写法应该为:
pStr.reSet(new string(“lalalal”));//ok
5. use_count
shared_ptr中有一个名为use_count()的成员函数,调用该函数会返回使用该对象的强引用(强引用理解为使用shared_ptr引用就可以)数量,如果该数量返回0时,则证明该对象将不再被使用,将会释放内存与资源。
当然除了use_count 为零外还有另外一种方式可以释放内存,那就是给shared_ptr赋值为nullptr。
6. 自定义删除器
当使用shard_ptr时,会提供一个默认的删除器,但是在某些情况下却需要我们自己定义删除器,此时我们会用到shared_ptr构造函数的第二个参数。
例:
shared_ptr<string> pNico(new string("nico"),
[](string* p)
{
cout << "delete " << *p << endl;
delete p;
});
默认的删除器只能删除指针,但是不能删除数组。
std::shared_ptr<int> p(new int[10]); //不能这么写,但是编译可以通过
也就是说,如果你要new 数组,就必须定义自己的删除器。
例如:
std::shared_ptr<int>pNptr(new int [10],[](int *p){delete[] p;});
除去自定义删除器还可以借助unique_ptr提供的一个工具delete[],
例如:
std::shared_ptr<int>pNico(new int[10],
std::default_delete<int[]>());
shared_ptr<int[]>p(new int[10]);//这样声明会报错。
std::unique_ptr<int[]> p(new int[10]); // OK
但是,如果使用unique_ptr声明删除器的话就必须给出第二个参数(函数指针)。
例如:
std::unique_ptr<int,void(*)(int *)>p(new int[10],[](int* p){delete[]p};
当然也可以定义其他的删除方式
例://删除临时文件
#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) {
}
void operator () (std::ofstream* fp)
{
fp->close(); // close.file
std::remove(filename.c_str()); // delete file
}
};
int main()
{
// create and open temporary file:
std::shared_ptr<std::ofstream> fp(new std::ofstream("tmpfile.txt"),
FileDeleter("tmpfile.txt"));
...
}
如果自定义删除方式,首先在该类中必须operate();而且这个的函数的参数必须与new的参数一致,而且用该方法可以多一个构造函数,也就是可以多一个参数,而remove函数需要文件名这个参数,所以我们构造函数需要将文件名称传递进去,这样就能关闭该文件流并将相应的文件删除了。