智能指针是什么?

智能指针对已有 C++ 指针的封装,方便内存管理。一般有三种智能指针,但使用中主要分为两类场景:

  1. unique_ptr:独占所有权,无法复制,只能 move()
  2. shared_ptrweak_ptr:共享与弱引用配合使用。
  • shared_ptr:引用计数,多指针共享同一资源。
  • weak_ptr:弱引用,不增加引用计数,通过 lock() 安全访问对象。

基础使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <iostream>
#include <memory>
#include <string>
#include <windows.h>


using namespace std;

// 前向声明
class A;
class B;
class C;

// 声明全局弱引用指针用于观察
weak_ptr<A> global_weak_a;
weak_ptr<B> global_weak_b;
weak_ptr<C> global_weak_c;

// 类定义
class B {
public:
shared_ptr<A> aPtr;
~B() {
cout << "B被销毁" << endl;
}
};

class A {
public:
shared_ptr<B> bPtr;
shared_ptr<C> cPtr;
~A() {
cout << "A被销毁" << endl;
}
};


class C {
public:
weak_ptr<A> aPtr; // 使用shared_ptr会导致循环引用

~C() {
cout << "C被销毁" << endl;
}
};

//=====================================================================
//Unique_ptr智能指针示例
void UniquePtrExample(){
//创建一个智能指针,管理一个动态分配的字符串对象
std::unique_ptr<std::string> strPtr = std::make_unique<std::string>("Hello, Smart Pointer!");
std::unique_ptr<std::string> strPtr2 = std::move(strPtr); //将strPtr的所有权转移给strPtr2
//std::unique_ptr<string> c = strPtr; // 语法错误:unique_ptr不能被复制,只能移动
//检查strPtr是否为空
if (strPtr == nullptr) {
std::cout << "strPtr is now empty after ownership transfer." << std::endl;
} else {
std::cout << "strPtr is not empty: " << *strPtr << std::endl;
}
std::cout<<"strPtr2 is not empty: " << *strPtr2 << std::endl;


}
//shared_ptr智能指针示例
void circularReferenceExample() {
std::cout << "---进入函数作用域---" << std::endl;
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();

std::cout << "创建后 - A的引用计数: " << a.use_count() << std::endl;
std::cout << "创建后 - C的引用计数: " << b.use_count() << std::endl;

a->bPtr = b; // B的引用计数加1
b->aPtr = a; // A的引用计数加1

std::cout << "设置互相引用后 - A的引用计数: " << a.use_count() << std::endl;
std::cout << "设置互相引用后 - B的引用计数: " << b.use_count() << std::endl;
std::cout << "---函数作用域即将结束---" << std::endl;

//函数结束后
//局部变量a被销毁,a的引用计数: 2->1
//局部变量b被销毁,b的引用计数: 2->1

}

//shared_ptr和weak_ptr智能指针示例
void break_circularReferenceExample() {

// 创建两个对象并互相引用
auto a = make_shared<A>();
cout << "A的引用计数: " << a.use_count() << endl;

auto c = make_shared<C>();
cout << "C的引用计数: " << c.use_count() << endl;

global_weak_a = a;
global_weak_c = c;

a->cPtr = c;
cout << "设置 a->bPtr 后,c的引用计数: " << c.use_count() << endl;

c->aPtr = a;
cout << "设置 c->aPtr 后,A的引用计数: " << a.use_count() << endl;

cout << "\n作用域结束前:" << endl;
cout << "A的引用计数: " << a.use_count() << endl;
cout << "C的引用计数: " << c.use_count() << endl;

cout << "\n作用域结束后" << endl;
// 此时由于循环引用,对象不会被销毁
//函数结束后
//局部变量a被销毁,a的引用计数: 1->0,
//致使指向c的指针被销毁,c的引用计数减一 2—>1,局部变量c被销毁,c的引用计数减一: 1->0
}

//=====================================================================
int main() {
// 设置控制台编码为 UTF-8
SetConsoleOutputCP(65001);

cout << "开始测试循环引用..." << endl;
//UniquePtrExample();
//circularReferenceExample();
//shared_ptr和weak_ptr智能指针示例
break_circularReferenceExample();
cout << "\n程序结束" << endl;

// 最后再次检查对象状态
auto final_a = global_weak_a.lock();
auto final_b = global_weak_b.lock();
auto final_c = global_weak_c.lock();
cout << "\n检查对象状态..." << endl;

cout << "\n程序结束时的状态:" << endl;
cout << "A对象: " << (final_a ? "仍然存在" : "已被销毁") << endl;
cout << "B对象: " << (final_b ? "仍然存在" : "已被销毁") << endl;
cout << "C对象: " << (final_c ? "仍然存在" : "已被销毁") << endl;

system("pause");
return 0;
}

游戏应用

场景管理父子关系、容器与元素之间的关系:

1
2
3
4
5
6
7
8
9
10
class Parent;
class Child;

class Parent {
shared_ptr<Child> child; // 强引用
};

class Child {
weak_ptr<Parent> parent; // 弱引用,防止循环引用
};

多线程安全

面试问题smart ptr 安全吗?

操作对象 线程安全 原因
引用计数 安全 内部使用原子操作
所指的对象数据 不安全 多个线程同时修改同一块内存
指针本身的指向 不安全 赋值操作非原子(包含更新数据指针和控制块指针
特性 1. 所指对象数据不安全 2. 指针本身的指向不安全
打个比方 几个人同时在一间教室里乱搬桌子。 几个人在抢夺教室的钥匙,把钥匙掰断了。
受损处 教室里的桌椅(对象数据)。 钥匙和锁(shared_ptr 本身的 16 字节内存)。
解决工具 在对象内部加 std::mutex 使用 std::atomic<shared_ptr> 或给 p1 加锁。
常见误区 以为引用计数是原子的,对象修改就安全。 以为 p1 只是一个指针(8字节),赋值是原子的。

https://www.cnblogs.com/KillerAery/p/9096558.html