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
|
// StrVec class, a class like vector but only store string as element
// CppPrimer 13.5
// lcl 20190712
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class StrVec {
public:
StrVec(): elements(nullptr), first_free(nullptr), cap(nullptr){}
StrVec(const StrVec &); // copy constructor
StrVec(StrVec &&) noexcept; // move constructor
StrVec &operator=(const StrVec&); // copy assignment
StrVec &operator=(StrVec&&) noexcept; //move assignment
~StrVec();
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:
std::allocator<std::string> alloc;
void chk_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; //point to first element
std::string *first_free; //point to first free element
std::string *cap; //point to rear;
};
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
pair<string*, string*>
StrVec::alloc_n_copy(const string *b, const string* e)
{
auto data = alloc.allocate(e-b);
return {data, uninitialized_copy(b,e,data)};
}
void StrVec::free()
{
if(elements){
for(auto p = first_free; p!=elements;alloc.destroy(--p));
alloc.deallocate(elements, cap-elements);
}
}
StrVec::StrVec(const StrVec&s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec()
{
free();
}
StrVec& StrVec::operator=(const StrVec &rhs)
{
// 先分配大小相同的内存拷贝,再释放掉原data
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
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)
// 使用了std::move,从而会调用移动构造函数
alloc.construct(dest++,std::move(*elem++));
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
StrVec::StrVec(StrVec &&s) noexcept //移动操作不抛出任何异常
:elements(s.elements), first_free(s.first_free), cap(s.cap)
{
// 令s进入这样的状态,对其进行析构是安全的?
// 这里令元素为空,析构函数将不能释放空间
// 因为元素已经转给移动后的对象,空间不应该释放,所以令他们为空
s.elements = s.first_free = s.cap = nullptr;
}
// 使用noexcept告知编译器不会抛出异常
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;
}
}
// test
int main(int argc, char* argv[])
{
StrVec sv,sv1;
std::cout << sv.size() << " "
<< sv.capacity() << std::endl;
sv.push_back("first line");
sv.push_back("second_line");
sv1 = sv;
std::cout << sv1.size() << " "
<< sv1.capacity() << std::endl;
}
|