智能指针std::shared_ptr初始化时可能泄露的地方

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针std::shared_ptr初始化时可能泄露的地方相关的知识,希望对你有一定的参考价值。

参考技术A 如果一个函数的参数需要一个智能指针,而我们在调用该函数时才生成该指针,而其他参数也有函数调用,如果其他参数的函数调用,出现异常,则会使用智能指针内存泄漏,例如:

上述调用,如果getParameter调用出现异常的话,std::shared_ptr<Tool>(new Tool)可能导致内存泄漏。

因为上述std::shared_ptr<Tool>(new Tool)执行分成两部分:

如getParameter()的指针穿插在上述两部之间,并出现异常,则new Tool的内存将被泄漏。

正确的做法,构造完std::shared_ptr之后,再调用:

更建议使用std::make_shared,因为std::make_shared的内存分配与std::shared_ptr的构造是在一起的:

[C++11]shared_ptr共享智能指针的初始化与使用

使用智能指针需要添加头文件:

代码如下:

#include <memory>

shared_ptr使用的注意事项:
1.不能使用一个原始地址初始化多个共享智能指针
2.函数不能返回管理了this的共享智能指针对象
3.共享智能指针不能循环引用

初始化:

1.通过构造函数初始化

代码如下:

std::shared_ptr<T> 智能指针名字(创建堆内存)

2.通过拷贝和移动构造函数初始化

代码如下:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1
    shared_ptr<int> ptr1(new int(520));
    cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
    //调用拷贝构造函数
    shared_ptr<int> ptr2(ptr1);
    cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
    shared_ptr<int> ptr3 = ptr1;
    cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
    //调用移动构造函数
    shared_ptr<int> ptr4(std::move(ptr1));
    cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl;
    std::shared_ptr<int> ptr5 = std::move(ptr2);
    cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl;

    return 0;
}



/*作者: 苏丙榅
链接: https://subingwen.cn/cpp/shared_ptr/#1-2-%E9%80%9A%E8%BF%87%E6%8B%B7%E8%B4%9D%E5%92%8C%E7%A7%BB%E5%8A%A8%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%88%9D%E5%A7%8B%E5%8C%96
来源: 爱编程的大丙*/

3.通过std::make_shared初始化

代码如下:

通过 C++ 提供的 std::make_shared() 就可以完成内存对象的创建并将其初始化给智能指针,函数原型如下:

template<typename T,typename ...Args>
shared_ptr<T>make_shared(Args&&...args);
  • T:模板参数的数据类型
  • Args&&… args :要初始化的数据,如果是通过 make_shared 创建对象,需按照构造函数的参数列表指定

4.通过reset方法初始化

共享智能指针类提供的 std::shared_ptr::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 );



/*作者: 苏丙榅
链接: https://subingwen.cn/cpp/shared_ptr/#1-3-%E9%80%9A%E8%BF%87std-make-shared%E5%88%9D%E5%A7%8B%E5%8C%96
来源: 爱编程的大丙*/
  • ptr:指向要取得所有权的对象的指针
  • d:指向要取得所有权的对象的指针
  • aloc:内部存储所用的分配器

reset两个功能:

1.让指向某块内存的智能指针解除对这片内存的管理

2.通过reset调用,让一个指向某块内存的智能指针指向另块一块内存,也就是让它去管理另外一块内存。

两个函数:

如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数 use_count,函数原型如下:

// 管理当前对象的 shared_ptr 实例数量,或若无被管理对象则为 0。
long use_count() const noexcept;

获取原始指针

对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的 get () 方法得到原始地址,其函数原型如下:

T* get() const noexcept;

代码如下:

#include <iostream>
#include <memory>
#include <string>
using namespace std;

class Test
{
public:
	Test()
	{
		cout << "construct" << endl;
	}
	Test(int x) :m_num(x)
	{
		cout << "construct x = " << x << endl;
	}
	Test(string str)
	{
		cout << "construct str = " << str << endl;
	}

	~Test()
	{
		cout << "destruct" << endl;
	}

	void setValue(int v)
	{
		m_num = v;
	}

	void print()
	{
		cout << "m_num = " << m_num << endl;
	}

private:
	int m_num;
};


int main()
{
	//通过构造函数初始化
	shared_ptr<int>ptr1(new int(3));
	cout << "ptr1 use_count = " << ptr1.use_count() << endl;
	//通过移动构造和拷贝构造函数初始化
	shared_ptr<int>ptr2 = move(ptr1);
	cout << "ptr1 use_count = " << ptr1.use_count() << endl;
	cout << "ptr2 use_count = " << ptr2.use_count() << endl;
	
	shared_ptr<int>ptr3 = ptr2;
	cout << "ptr3 use_count = " << ptr3.use_count() << endl;
	cout << "ptr2 use_count = " << ptr2.use_count() << endl;

	//通过std::make_shared初始化
	shared_ptr<int>ptr4 = make_shared<int>(8);
	shared_ptr<Test>ptr5 = make_shared<Test>(8);
	cout << "ptr5 use_count = " << ptr5.use_count() << endl;
	shared_ptr<Test>ptr6 = make_shared<Test>("hello");
	cout << "ptr6 use_count = " << ptr6.use_count() << endl;
	//通过reset初始化
	ptr6.reset();
	cout << "ptr6 use_count = " << ptr6.use_count() << endl;
	ptr5.reset(new Test("world"));
	cout << "ptr5 use_count = " << ptr5.use_count() << endl;
	return 0;
}

测试结果:

在这里插入图片描述

shared_ptr的使用:

代码如下:

#include <iostream>
#include <memory>
#include <string>
using namespace std;

class Test
{
public:
	Test()
	{
		cout << "construct" << endl;
	}
	Test(int x) :m_num(x)
	{
		cout << "construct x = " << x << endl;
	}
	Test(string str)
	{
		cout << "construct str = " << str << endl;
	}

	~Test()
	{
		cout << "destruct" << endl;
	}

	void setValue(int v)
	{
		m_num = v;
	}

	void print()
	{
		cout << "m_num = " << m_num << endl;
	}

private:
	int m_num;
};


int main()
{
	//通过构造函数初始化
	shared_ptr<int>ptr1(new int(3));
	cout << "ptr1 use_count = " << ptr1.use_count() << endl;
	//通过移动构造和拷贝构造函数初始化
	shared_ptr<int>ptr2 = move(ptr1);
	cout << "ptr1 use_count = " << ptr1.use_count() << endl;
	cout << "ptr2 use_count = " << ptr2.use_count() << endl;
	
	shared_ptr<int>ptr3 = ptr2;
	cout << "ptr3 use_count = " << ptr3.use_count() << endl;
	cout << "ptr2 use_count = " << ptr2.use_count() << endl;

	//通过std::make_shared初始化
	shared_ptr<int>ptr4 = make_shared<int>(8);
	shared_ptr<Test>ptr5 = make_shared<Test>(8);
	cout << "ptr5 use_count = " << ptr5.use_count() << endl;
	shared_ptr<Test>ptr6 = make_shared<Test>("hello");
	cout << "ptr6 use_count = " << ptr6.use_count() << endl;
	//通过reset初始化
	ptr6.reset();
	cout << "ptr6 use_count = " << ptr6.use_count() << endl;
	ptr5.reset(new Test("world"));
	cout << "ptr5 use_count = " << ptr5.use_count() << endl;


	//获取原始指针
	Test *t = ptr5.get();
	t->setValue(1000);
	t->print();

	//直接使用
	ptr5->setValue(999);
	ptr5->print();

	return 0;
}

以上是关于智能指针std::shared_ptr初始化时可能泄露的地方的主要内容,如果未能解决你的问题,请参考以下文章

C++智能指针

C++智能指针

智能指针std::shared_ptr总结与使用

使用std智能指针的正确方法来确保ptr安全

c++11智能指针(一) shared_ptr

C++11常用知识