自定义内存管理类StrVec的实现

没写成模板,针对string。

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
#include <string>
#include <memory>
using std::string;
class StrVec
{
public:
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
StrVec(const StrVec&);
StrVec(StrVec&&) noexcept; // 移动构造函数

~StrVec();

StrVec& operator=(const StrVec&);
StrVec& operator=(StrVec&&) noexcept; // 移动赋值运算符
StrVec& operator=(std::initializer_list<std::string>);

std::string& operator[](std::size_t n) { return elements[n]; }
const std::string& operator[](std::size_t n) const { return elements[n]; }

void push_back(const std::string&);
void push_back(const std::string&&);

size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }

std::string* begin() const { return elements; }
std::string* end() const { return first_free; }

private:
static std::allocator<std::string> alloc;

void check_n_alloc() { if (size() == capacity()) reallocate(); }
std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);
void free();
void reallocate();

std::string* elements; // 指向数组首元素的指针
std::string* first_free; // 指向数组第一个空闲元素的指针
std::string* cap; // 指向数组尾后位置的指针
};

std::allocator<std::string> StrVec::alloc;

StrVec::StrVec(const StrVec& s)
{
// 调用alloc_n_copy分配空间以容纳与s中一样多的元素
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}

StrVec::StrVec(StrVec&&s) noexcept // 移动操作不应抛出任何异常
: elements(s.elements), first_free(s.first_free), cap(s.cap) // 成员初始化器接管s中的资源
{
// 令s进入这样的状态——对其运行析构函数是安全的
// 不然销毁源会释放掉我们刚刚移动的内存
s.elements = s.first_free = s.cap = nullptr;
}

StrVec::~StrVec()
{
free();
}

StrVec& StrVec::operator=(const StrVec& rhs)
{
// 调用alloc_n_copy分配内存,大小与rhs中元素占用空间一样多
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}

StrVec& StrVec::operator=(StrVec&& rhs) noexcept
{
// 直接检测自赋值
if (this != &rhs)
{
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}

StrVec& StrVec::operator=(std::initializer_list<std::string> il)
{
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}

void StrVec::push_back(const std::string& s)
{
// 确保有空间容纳新元素
check_n_alloc();
// 在first_free指向的元素中构造s的副本
alloc.construct(first_free++, s);
}

void StrVec::push_back(const std::string&& s)
{
check_n_alloc();
alloc.construct(first_free++, std::move(s));
}

std::pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string* b, const std::string* e)
{
// 分配空间保存给定范围中的元素
// 尾后指针减去首元素指针,来计算需要多少空间。
auto data = alloc.allocate(e - b);
// 初始化并返回一个pair, 该pair由data和uninitialized_copy的返回值构成
return{ data, uninitialized_copy(b, e, data) };
}

void StrVec::free()
{
// 不能传递给deallocate一个空指针,如果elements为0,函数什么也不做
if (elements)
{
// 逆序销毁旧元素
for (auto p = first_free; p != elements; /*空*/ )
{
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}

void StrVec::reallocate()
{
// 将分配当前大小两倍的内存空间
auto newcapacity = size() ? 2 * size() : 1;
// 分配新内存
auto newdata = alloc.allocate(newcapacity);
// 将数据从旧内存移动到新内存
auto dest(newdata); // 指向新数组中下一个空闲位置
auto elem(elements); // 指向旧数组中下一个元素
for (size_t i = 0; i != size(); ++i)
{
alloc.construct(dest++, std::move(*elem++));
}
free(); // 一旦移动完元素就释放旧内存空间
// 更新我们的数据结构,指向新元素
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}