51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

C++ 智能指针 shared ptr

C++ 的 shared_ptr 是 C++11 标准引入的智能指针之一,用于管理动态分配的对象的所有权。它允许多个 shared_ptr 实例共享对同一对象的所有权,而不会出现内存泄漏或者悬空指针的情况。shared_ptr 使用引用计数技术来跟踪有多少个 shared_ptr 实例指向同一个对象,并在最后一个实例销毁时自动释放对象。

接下来,主要从以下几个方面来学习:

  1. shared_ptr 使用

  2. shared_ptr 特性

  3. shared_ptr 引用计数

  4. shared_ptr 自定义删除器

  5. shared_ptr 使用 {#title-0} ===========================

shared_ptr 叫做共享智能指针,其创建和使用语法如下:

#if 0
#include <iostream>
using namespace std;

class Person { public: Person() { cout << "无参构造函数" << endl; } Person(int, int) { cout << "有参构造函数" << endl; }

~Person()
{
	cout &lt;&lt; &quot;析构函数&quot; &lt;&lt; endl;
}

};

// 1. 创建 shared_ptr 对象 void test01() { // 1.1 使用 shared_ptr 的构造函数创建对象 shared_ptr<Person> sp1(new Person(10, 20));

// 1.2 使用 shared_ptr 管理动态对象数组
shared_ptr&lt;Person[]&gt; sp2(new Person[5]);

}

// 2. shared_ptr 操作函数 void test02() { shared_ptr<Person> sp1(new Person(10, 20)); shared_ptr<Person> sp2(new Person(100, 200));

// 2.1 get 成员函数可以获得 shared_prt 管理的动态对象指针
Person* person = sp1.get();

// 2.2 swap 成员函数可以交换两个 shared_ptr 管理的动态对象指针
sp1.swap(sp2);

// 2.3 reset 成员函数存在两个重载版本的函数,其作用分别如下:
sp1.reset();  // 释放其管理动态指针,此时 sp1 对象管理的动态指针指向为 nullptr
sp1.reset(new Person(1, 2));  // 释放原来的动态对象,并指向新的动态对象

}

int main() { test01(); test02();

return 0;

}

#endif

  1. shared_ptr 特性 {#title-1} ===========================

shared_ptr 叫做共享智能指针,多个 shared_ptr 对象能够同时持有并管理同一个动态对象。当 shared_ptr 发生对象拷贝、赋值时都会导致多个 shared_ptr 对象持有同一个动态对象,如下代码所示:

#if 1
#include <iostream>
#include <vector>
using namespace std;

class Person { public: Person() { cout << "无参构造函数" << endl; }

~Person()
{
	cout &lt;&lt; &quot;析构函数&quot; &lt;&lt; endl;
}

};

void test() { shared_ptr<Person> sp1(new Person);

// 1. 允许对象拷贝、对象赋值
shared_ptr&lt;Person&gt; sp2(sp1);

shared_ptr&lt;Person&gt; sp3;
sp3 = sp1;  // 对象赋值

// 2. 允许对象移动拷贝、移动赋值
shared_ptr&lt;Person&gt; sp4 = move(sp3);

shared_ptr&lt;Person&gt; sp5;
sp5 = move(sp1);  // 移动赋值

cout &lt;&lt; &quot;sp1:&quot; &lt;&lt; sp1.get() &lt;&lt; endl;  // 已被移动
cout &lt;&lt; &quot;sp2:&quot; &lt;&lt; sp2.get() &lt;&lt; endl;
cout &lt;&lt; &quot;sp3:&quot; &lt;&lt; sp3.get() &lt;&lt; endl;  // 已被移动
cout &lt;&lt; &quot;sp4:&quot; &lt;&lt; sp4.get() &lt;&lt; endl;
cout &lt;&lt; &quot;sp5:&quot; &lt;&lt; sp5.get() &lt;&lt; endl;

// 允许存储到容器中
vector&lt;shared_ptr&lt;Person&gt;&gt; vec;
vec.push_back(sp2);
vec.push_back(sp4);
vec.push_back(sp5);

}

int main() { test(); return 0; }

#endif

  1. 引用计数 {#title-2} ==================

有些同学可能想:多个 shared_ptr 管理同一个 Person 对象,当某个 shared_ptr 生命周期结束,不会导致其他的 shared_ptr 管理了无效指针吗?

答案是不会的。这是因为 shared_ptr 是通过引用计数的方式来实现对象共享的。那么,什么是引用计数呢?

在 shared_ptr 对象的内部维护了两个非常重要的内容:

  1. 动态创建对象
  2. 引用计数对象

请看下面的示例代码:

#if 1
#include <iostream>
using namespace std;

struct Person { Person() { cout << "Person 构造函数" << endl; }

~Person()
{
	cout &lt;&lt; &quot;Person 析构函数&quot; &lt;&lt; endl;
}

};

void test() { // 初始化智能指针,引用计数为 0 shared_ptr<Person> sp1(new Person); cout << "sp1:" << sp1.use_count() << endl;

// 发生拷贝,引用计数 +1
shared_ptr&lt;Person&gt; sp2(sp1);
cout &lt;&lt; &quot;sp2:&quot; &lt;&lt; sp2.use_count() &lt;&lt; endl;

// 发生赋值,引用计数 + 1
shared_ptr&lt;Person&gt; sp3;
sp3 = sp2;
cout &lt;&lt; &quot;sp3:&quot; &lt;&lt; sp3.use_count() &lt;&lt; endl;

// 判断是否独占资源
cout &lt;&lt; &quot;sp1 是否独占资源:&quot; &lt;&lt; sp1.unique() &lt;&lt; endl;

// sp2 释放资源所有权,通过该对方访问的引用次数为 0
sp2.reset();
cout &lt;&lt; &quot;sp2:&quot; &lt;&lt; sp2.use_count() &lt;&lt; endl;

// sp1 和 sp2 引用计数为 2
cout &lt;&lt; &quot;sp1:&quot; &lt;&lt; sp1.use_count() &lt;&lt; endl;
cout &lt;&lt; &quot;sp3:&quot; &lt;&lt; sp3.use_count() &lt;&lt; endl;

}

int main() { test(); return 0; } #endif

Person 构造函数
sp1:1
sp2:2
sp3:3
sp1 是否独占资源:0
sp2:0
sp1:2
sp3:2
Person 析构函数
  1. shared_ptr 自定义删除器 {#title-3} ===============================

shared_ptr 和 unique_ptr 一样,并不仅仅管理 new 出来的动态对象,但是,智能指针默认使用 delete、delete[] 来释放被管理的对象。

此时,如果被管理的对象并不是 new、new[] 出来的,需要自定义删除器。其删除器可以是以下任何一种形式:

  1. 普通函数
  2. 函数对象
  3. lambda 匿名函数对象

请看下面的示例代码:

#if 1
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <memory>
using namespace std;

void my_deleter(FILE* fp) { cout << "文件自动关闭" << endl; fclose(fp); fp = nullptr; }

struct MyDeleter { void operator()(FILE* fp) { cout << "文件自动关闭" << endl; fclose(fp); fp = nullptr; } };

void test() { // 1. 使用普通函数作为自定义删除器 shared_ptr<FILE> sp1(fopen("./demo.txt", "w"), my_deleter);

// 2. 使用函数对象作为自定义删除器
shared_ptr&lt;FILE&gt; sp2(fopen(&quot;./demo.txt&quot;, &quot;w&quot;), MyDeleter());

// 3. 使用 lambda 匿名函数对象作为自定义删除器
shared_ptr&lt;FILE&gt; sp3(fopen(&quot;./demo.txt&quot;, &quot;w&quot;), [](FILE* fp) {
		cout &lt;&lt; &quot;文件自动关闭&quot; &lt;&lt; endl;
		fclose(fp);
		fp = nullptr;
	});

}

int main() { test(); return 0; }

#endif

至此,shared_ptr 智能指针讲解完毕,希望对你有所帮助!

赞(3)
未经允许不得转载:工具盒子 » C++ 智能指针 shared ptr