抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

关于智能指针

  • 在C++11中通过引入智能指针的概念,使得C++程序员不需要手动释放内存
  • 智能指针的种类
    • std::unique_ptr (独占指针)
    • std::shared_ptr (计数指针)
    • std::weak_ptr (shared_ptr 的补充)
  • 注意
    • std::auto_ptr 已废弃

智能指针概述

  • C++的指针包括两种

    • 原始指针 (raw pointer)
    • 智能指针
  • 智能指针是原始指针的封装,其优点是会自动分配内存,不用担心潜在的内存泄露

智能指针与原始指针

  • 并不是所有的指针都可以封装成智能指针,很多时候原始指针要更方便
  • 各种指针中,最常用的是裸指针,其次是unique_ptr和shared_ptr
  • weak_ptr是share_ptr的一个补充应用场景较少

智能指针与Rust的内存安全

  • 智能指针只解决一部分问题,即独占/共享所有权指针的释放、传输
  • 智能指针没有从根本上解决C++内存安全问题,不加以注意依然会造成内存安全问题

独占指针: unique_ptr

  • unique_ptr
    • 在任何给定的时刻,只能有一个指针管理内存
  • 当指针超出作用域时,内存自动释放
  • 该类型指针不可Copy,只可以Move

创建方式

  • 三种创建方式
    1. 通过已有的裸指针创建 (建议设置成空销毁掉)
    2. 通过new来创建 (与第一种没有区别)
    3. 通过std::make_unique创建 (推荐)
  • unique_ptr可以通过get()获取地址
  • unique_ptr实现->与*
    • 可以通过->调用成员函数
    • 可以通过*调用dereferencing

代码实现

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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*************************************************************************
> File Name: unique_ptr_test.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年08月02日 星期二 09时44分12秒
************************************************************************/

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

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(test1)

class Cat {
public :
Cat(string Name);
Cat() = default;

void cat_info() const {
cout << "cat info name : " << name << endl;
}
string get_name() const {
return this->name;
}
void set_cat_name(const string &name) {
this->name = name;
}

~Cat();

private :
string name{"ttw"};

};

Cat::Cat(string Name) : name(Name) {
cout << "constructor of Cat : " << name << endl;
}

Cat::~Cat() {
cout << "Destructor of Cat : " << name << endl;
}

int main() {
/*
Cat c1("ok");
c1.cat_info();
{
// 作用域结束自动销毁
Cat c1{"OK"};
c1.cat_info();
}
*/

// 原生
Cat *c2 = new Cat("no");
c2->cat_info();
delete c2;
{
Cat *c2 = new Cat("no_no");
c2->cat_info();
delete c2;
}
return 0;
}

ENDS(test1)

BEGINS(test2)

class Cat {
public :
Cat(string Name);
Cat() = default;

void cat_info() const {
cout << this << " : cat info name : " << name << endl;
}
string get_name() const {
return this->name;
}
void set_cat_name(const string &name) {
this->name = name;
}

~Cat();

private :
string name{"ttw"};

};

Cat::Cat(string Name) : name(Name) {
cout << this << " : constructor of Cat : " << name << endl;
}

Cat::~Cat() {
cout << this << " : Destructor of Cat : " << name << endl;
}

int main() {
Cat *c1 = new Cat("c1");
unique_ptr<Cat> u_c1(c1);
/*
// 建议销毁 Cat *c1 = new Cat(""); 否则如下
// c1 还能用么? 可以用!
c1->cat_info();
u_c1->cat_info();
c1->set_cat_name("ok");
u_c1->cat_info();
*/

//delete c1;
// c1 = nullptr;
u_c1->cat_info();

cout << "------------------ 分隔符 ------------------------" << endl;

// 第二种、new
unique_ptr<Cat> u_c2(new Cat("c2"));
unique_ptr<int> u_i_p2{new int(100)};
u_c2->cat_info();
u_c2->set_cat_name("u_c2");
u_c2->cat_info();
cout << *u_i_p2 << endl;
cout << "int address : " << u_i_p2.get() << endl;
cout << "Cat c2 address : " << u_c2.get() << endl;

cout << "------------------ 分隔符 ------------------------" << endl;

// 第三种、推荐
// unique_ptr<Cat> p = make_unique<Cat>();
unique_ptr<Cat> u_c3 = make_unique<Cat>();
unique_ptr<int> u_i_p3 = make_unique<int>(200);
u_c3->cat_info();
u_c3->set_cat_name("u_c3");
u_c3->cat_info();
cout << *u_i_p3 << endl;
cout << "int address : " << u_i_p3.get() << endl;
cout << "Cat c3 address : " << u_c3.get() << endl;

// get 和 常量类型

return 0;
}

ENDS(test2)

int main() {
// test1::main();
test2::main();
return 0;
}

unique_ptr与函数调用

  • unique_ptr是不可Copy,只可以Move
  • 在做函数参数或是返回值中一定注意所有权

函数调用与unique_ptr注意事项

  • Passing by value
    • 需要用std::move来转移内存权限
    • 如果参数值传入std::make_unique语句 自动转为move
  • Passing by reference(传引用)
    • 如果设置参数为const则不能改变指向
      • 比如说reset()
    • reset()方法为智能指针清空方法
  • Return by value
    • 指向一个local object
    • 可以用作链式函数

计数指针: shared_ptr

Ps:看看析构几次!!!!!!!!!!!!!

销毁是按照内存来的、它数据只有一套只会销毁一次

  • shared_ptr计数指针又称共享指针

  • 与unique_ptr不同的是它可以共享数据的

  • shared_ptr创建了一个计数器与类对象所指的内存相关联

  • Copy则计数器加一,销毁则计数器减一

  • api为use_count()

shared_ptr与函数

  • shared_ptr passed by value
    • copy
    • 函数内部计数器加一
  • shared_ptr passed by ref
    • const表示不可改变指向
  • retuming by value
    • 链式调用

代码实现

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
/*************************************************************************
> File Name: shared_ptr_test.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年08月02日 星期二 18时01分01秒
************************************************************************/

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

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class Cat {
public :
Cat(string Name);
Cat() = default;

void cat_info() const {
cout << this << " : cat info name : " << name << endl;
}
string get_name() const {
return this->name;
}
void set_cat_name(const string &name) {
this->name = name;
}

~Cat();

private :
string name{"ttw"};

};

Cat::Cat(string Name) : name(Name) {
cout << this << " : constructor of Cat : " << name << endl;
}

Cat::~Cat() {
cout << this << " : Destructor of Cat : " << name << endl;
}


ENDS(ttw)

BEGINS(test1)


int main() {
// 常量类型
shared_ptr<int> s_p1 = make_shared<int>(10);
// shared_ptr<int> s_p1 = make_shared<int>(new int (10));
cout << "s_p1 value : " << *s_p1 << endl;
cout << "s_p1 use count : " << s_p1.use_count() << endl;

// copy
shared_ptr<int> s_p2 = s_p1;
cout << "s_p2 value : " << *s_p2 << endl;
cout << "s_p2 use count : " << s_p2.use_count() << endl;

*s_p2 = 30;
cout << "*s_p2 = 30; out s_p1 value : " << *s_p1 << endl;
cout << "*s_p2 = 30; out s_p2 value : " << *s_p2 << endl;

shared_ptr<int> s_p3 = s_p1;
s_p1 = nullptr;
cout << "s_p1 use_count : " << s_p1.use_count() << endl;
cout << "s_p2 use_count : " << s_p2.use_count() << endl;
cout << "s_p3 use_count : " << s_p3.use_count() << endl;
cout << "------------------- 分隔符 -------------------------" << endl;
// 自定义类型
shared_ptr<ttw::Cat> c_p1 = make_shared<ttw::Cat>();
cout << "c_p1 use-count : " << c_p1.use_count() << endl;

shared_ptr<ttw::Cat> c_p2 = c_p1;
shared_ptr<ttw::Cat> c_p3 = c_p1;
cout << "c_p1 use-count : " << c_p1.use_count() << endl;
cout << "c_p2 use-count : " << c_p2.use_count() << endl;
cout << "c_p3 use-count : " << c_p3.use_count() << endl;

c_p1.reset();
cout << "reset c_p1 use-count : " << c_p1.use_count() << endl;
cout << " c_p2 use-count : " << c_p2.use_count() << endl;
cout << " c_p3 use-count : " << c_p3.use_count() << endl;

return 0;
}


ENDS(test1)

BEGINS(test2)

void cat_by_value(shared_ptr<ttw::Cat> cat) {
cout << cat->get_name() << endl;
cat->set_cat_name("qwe");
cout << "func use count : " << cat.use_count() << endl;
return ;
}

void cat_by_ref(const shared_ptr<ttw::Cat> &cat) {
cout << cat->get_name() << endl;
// cat.reset();
cout << "func use count : " << cat.use_count() << endl;
return ;
}

int main() {

// Passing by value

shared_ptr<ttw::Cat> c_p1 = make_shared<ttw::Cat>("dog");
// 就不用想unique_ptr move了
cat_by_value(c_p1);
cout << "c_p1 use count : " << c_p1.use_count() << endl;
c_p1->cat_info();


// Passing by reference
cat_by_ref(c_p1);
cout << "c_p1 use count : " << c_p1.use_count() << endl;


return 0;
}

ENDS(test2)


int main() {
// shared_ptr 常量类型、自定义类型
// test1::main();
test2::main();
return 0;
}

shared_ptr与unique_ptr

  • 不能将shared_ptr转换为unique_ptr
  • unique_ptr可以转换为shared_ptr
    • 通过std::move

常见的设计模式

  • 将你的函数返回unique_ptr是一种常见的设计模式,这样可以提高代码的复用度,你可以随时改变为shared_ptr

unique_ptr与shared_ptr代码实现

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
/*************************************************************************
> File Name: shared_ptr_test.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年08月02日 星期二 18时01分01秒
************************************************************************/

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

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class Cat {
public :
Cat(string Name);
Cat() = default;

void cat_info() const {
cout << this << " : cat info name : " << name << endl;
}
string get_name() const {
return this->name;
}
void set_cat_name(const string &name) {
this->name = name;
}

~Cat();

private :
string name{"ttw"};

};

Cat::Cat(string Name) : name(Name) {
cout << this << " : constructor of Cat : " << name << endl;
}

Cat::~Cat() {
cout << this << " : Destructor of Cat : " << name << endl;
}


ENDS(ttw)

BEGINS(test1)

unique_ptr<ttw::Cat> get_unique_ptr() {
unique_ptr<ttw::Cat> cat_p = make_unique<ttw::Cat>("local cat");
return cat_p;
}

int main() {

unique_ptr<ttw::Cat> c_p1 = make_unique<ttw::Cat>("qwe");
shared_ptr<ttw::Cat> c_p2 = move(c_p1);

// cout << "c_p1(unioque) use count : " << c_p1.use_count();
cout << "c_p2(shared) use count : " << c_p2.use_count() << endl;

// func
shared_ptr<ttw::Cat> c_p3 = get_unique_ptr();
// 转换成功
if(c_p3) {
c_p3->cat_info();
cout << "c_p3(shared) use count : " << c_p3.use_count() << endl;
}

return 0;
}

ENDS(test1)


int main() {
test1::main();
return 0;
}

weak_ptr(弱引用)

  • weak-ptr并不拥有所有权
  • 并不能调用->和解引用*

weak_ptr为什么会存在

  • A类中有一个需求需要存储其他A类对象的信息
  • 如果使用shared_ptr,那么在销毁时会遇到循环依赖问题(cyclic dependency problem)
  • 所以我们这里需要用一个不需要拥有所有权的指针来标记该同类对象
    • weak_ptr可以通过lock()函数来提升为shared_ptr(类型转换)

代码实现

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
/*************************************************************************
> File Name: weak_ptr_test.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年08月02日 星期二 09时44分12秒
************************************************************************/

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

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class Cat {
public :
Cat(string Name);
Cat() = default;

void cat_info() const {
cout << this << " : cat info name : " << name << endl;
}
string get_name() const {
return this->name;
}
void set_cat_name(const string &name) {
this->name = name;
}

void set_friend(shared_ptr<Cat> c) {
m_friend = c;
}

~Cat();

private :
string name{"ttw"};
// shared_ptr<Cat> m_friend; // 修改weak
weak_ptr<Cat> m_friend;
};

Cat::Cat(string Name) : name(Name) {
cout << this << " : constructor of Cat : " << name << endl;
}

Cat::~Cat() {
cout << this << " : Destructor of Cat : " << name << endl;
}


ENDS(ttw)


BEGINS(test1)

int main() {
shared_ptr<ttw::Cat> s_p1 = make_shared<ttw::Cat>();
weak_ptr<ttw::Cat> w_p1(s_p1);

// use count
cout << "w_p1 use count : " << w_p1.use_count() << endl;
cout << "s_p1 use count : " << s_p1.use_count() << endl;

shared_ptr<ttw::Cat> s_p2 = w_p1.lock();
cout << "w_p1 use count : " << w_p1.use_count() << endl;
cout << "s_p1 use count : " << s_p1.use_count() << endl;
cout << "s_p2 use count : " << s_p2.use_count() << endl;

cout << "-------------------- 分割线 --------------------------" << endl;

// 环形引用问题
shared_ptr<ttw::Cat> c3 = make_shared<ttw::Cat>("c3");
shared_ptr<ttw::Cat> c4 = make_shared<ttw::Cat>("c4");
c3->set_friend(c3);
c4->set_friend(c4);


return 0;
}

ENDS(test1)

int main() {
test1::main();
return 0;
}

评论