0%

C11:智能指针

这篇文章记述了关于C++11 智能指针的知识点。

动态内存

到目前为止,我们编写的程序中所使用的对象都有着严格定义的生存期。全局对象在程序启动时分配,在程序结束时销毁。对于局部自动对象,当我们进入其定义所在的程序块时被创建,在离开块时被销毁。局部static对象在第一次使用前分配,在程序结束时销毁。

除了自动和static对象外,C++还支持动态分配对象。动态分配的对象的生存期与它们在哪里创建是无关的,只有当显式地被释放时,这些对象才会销毁。

动态对象的正确释放被证明是编程中极易出错的地方。为了更安全的使用动态对象,标准库定义了两个智能指针类型来管理动态分配的对象。当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它。

我们的程序到目前为止只使用过静态内存或栈内存。静态内存用来保存局部static对象:static int counter 函数调用次数。类static数据成员以及定义在任何函数之外的变量。栈内存用来保存定义在函数内的非static对象。分配在静态或栈内存中的对象由编译器自动创建和销毁。对于栈对象,仅在其定义的程序块运行时才存在:static对象在使用之前分配,在程序结束时销毁。

除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称为自由空间或堆。程序用堆来存储动态分配的对象—即那些在程序运行时分配的对象。动态对象的生存期由程序来控制,也就是说,当动态对象不再使用时,我们的代码必须显式地销毁它们。

类static数据成员

AE4A99F468247A2465144993271D3C22

有的时候类需要它的一些成员与类本身直接相关,而不是和类的各个对象保持关联。

例如,一个银行账户类需要一个数据成员来表示当前的基准利率。在此例中,我们希望利率与类关联,而非与类的每个对象关联。从实现效率的角度来看,没必要为每个对象都储存利率信息。而且更加中重要的是,一旦利率浮动,我们希望所有的对象都能使用新的值。

1
2
3
4
5
6
7
8
9
10
11
class Account{
public calculate(){amount += amount * interestRate;}
static double rate() {return interestRate;}
static void rate(double);

private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
}

类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据。因此,每个Account对象将包含两个数据成员:owner和amount。只存在一个interestRate对象而且它被所有Account对象共享。

动态内存与智能指针

C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化;delete,接受一个动态对象指针,销毁该对象,并释放与之相关联的内存。

为了更容易同时更安全地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr则独占所指向的对象。此外还有一个weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。避免两个shared_ptr相互引用造成死锁的局面。

shared_ptr

shared_ptr p1;

shared_ptr<list> p2;

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
#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>

using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}

int value;

};

int main()
{
std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1

std::shared_ptr<Person> p2 = std::make_shared<Person>(2);

p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针

std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2

p1.reset();//Person(3)的引用计数为1
p3.reset();//Person(3)的引用计数为0,析构Person(3)
return 0;
}

reset()包含两个操作。当智能指针中有值的时候,调用reset()会使引用计数减1.当调用reset(new xxx())重新赋值时,智能指针首先是生成新对象,然后将就对象的引用计数减1(当然,如果发现引用计数为0时,则析构旧对象),然后将新对象的指针交给智能指针保管。

手写智能指针

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
#pragma once

template<class T>
class SharedPointer
{
public:
//默认构造函数,内部指针,未指向任何资源,引用计数为0,因为它未与任何资源绑定
SharedPointer() :m_refCount(nullptr), m_pointer(nullptr) {}

//构造函数,初始化时,指向一个已经分配好的资源
SharedPointer(T* adoptTarget) :m_refCount(nullptr), m_pointer(adoptTarget)
{
addReference();
}

//构造函数,使用其它对象创建新对象
SharedPointer(const SharedPointer<T>& copy)
:m_refCount(copy.m_refCount), m_pointer(copy.m_pointer)
{
addReference();
}

//析构函数,引用计数递减,当为0时,释放资源
virtual ~SharedPointer()
{
removeReference();
}

//赋值操作
//当左值被赋值时,表明它不再指向所指的资源,故引用计数减一
//之后,它指向了新的资源,所以对应这个资源的引用计数加一
SharedPointer<T>& operator=(const SharedPointer<T>& that)
{
if (this != &that)
{
removeReference();
this->m_pointer = that.m_pointer;
this->m_refCount = that.m_refCount;
addReference();
}
return *this;
}

//判断是否指向同一个资源
bool operator==(const SharedPointer<T>& other)
{
return m_pointer == other.m_pointer;
}
bool operator!=(const SharedPointer<T>& other)
{
return !operator==(other);
}

//指针解引用
T& operator*() const
{
return *m_pointer;
}
//调用所知对象的公共成员
T* operator->() const
{
return m_pointer;
}

//获取引用计数个数
int GetReferenceCount() const
{
if (m_refCount)
{
return *m_refCount;
}
else
{
return -1;
}
}

protected:
//当为nullpter时,创建引用计数资源,并初始化为1
//否则,引用计数加1。
void addReference()
{
if (m_refCount)
{
(*m_refCount)++;
}
else
{
m_refCount = new int(0);
*m_refCount = 1;
}
}

//引用计数减一,当变为0时,释放所有资源
void removeReference()
{
if (m_refCount)
{
(*m_refCount)--;
if (*m_refCount == 0)
{
delete m_refCount;
delete m_pointer;
m_refCount = 0;
m_pointer = 0;
}
}
}

private:
int* m_refCount;
T* m_pointer;
};

Welcome to my other publishing channels