智能指针之shared_ptr

https://www.e-learn.cn/content/qita/725263

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函数需要文件名这个参数,所以我们构造函数需要将文件名称传递进去,这样就能关闭该文件流并将相应的文件删除了。

猜你喜欢

转载自blog.csdn.net/az44yao/article/details/94186553