COMP6771学习笔记

在UNSW转码一年,第四学期接触C++,有很多不适应的地方,记一点平时可能会忘的东西。

期末考试临时知识点备忘

Container

1. std::vector

std::vector 是一个动态数组,可以根据需要动态调整大小。

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
#include <vector>
#include <iostream>
#include <ranges>

int main() {
std::vector<int> v; // 默认构造函数
v.push_back(1); // 添加元素
v.emplace_back(2); // 添加元素,性能比push_back更好
v.pop_back(); // 删除最后一个元素
v.size(); // 返回元素数量
v.capacity(); // 返回当前容量
v[0]; // 访问元素
v.at(0); // 访问元素(带范围检查)
v.clear(); // 清空所有元素
v.begin(); // 返回迭代器,指向第一个元素
v.end(); // 返回迭代器,指向最后一个元素的下一个位置
v.shrink_to_fit(); // 减少容量至当前大小
v.reserve(10); // 预留空间
v.resize(5); // 调整大小
std::ranges::sort(v); // 使用ranges进行排序

for (const auto& elem : v) {
std::cout << elem << " "; // 遍历元素
}
std::cout << std::endl;
}

2. std::queue

std::queue 是一个FIFO(先进先出)数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <queue>
#include <iostream>

int main() {
std::queue<int> q;
q.push(1); // 入队
q.push(2);
q.pop(); // 出队
q.front(); // 返回队头元素
q.back(); // 返回队尾元素
q.empty(); // 检查队列是否为空
q.size(); // 返回队列中元素的数量

while (!q.empty()) {
std::cout << q.front() << " ";
q.pop(); // 遍历并出队元素
}
std::cout << std::endl;
}

3. std::deque

std::deque 是一种双端队列,可以在两端高效地插入和删除元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <deque>
#include <iostream>

int main() {
std::deque<int> d;
d.push_back(1); // 在尾部添加元素
d.push_front(2); // 在头部添加元素
d.pop_back(); // 删除尾部元素
d.pop_front(); // 删除头部元素
d.front(); // 返回头部元素
d.back(); // 返回尾部元素
d.size(); // 返回元素数量
d.clear(); // 清空所有元素
d.resize(5); // 调整大小

for (const auto& elem : d) {
std::cout << elem << " "; // 遍历元素
}
std::cout << std::endl;
}

4. std::map

std::map 是一种关联容器,存储键值对,所有元素按照键值排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <map>
#include <iostream>

int main() {
std::map<int, std::string> m;
m.insert({1, "one"}); // 插入元素
m[2] = "two"; // 使用[]操作符插入或修改元素
m.erase(1); // 删除键为1的元素
m.find(2); // 查找键为2的元素
m.size(); // 返回元素数量
m.clear(); // 清空所有元素
m.contains(2); // 检查键是否存在

for (const auto& [key, value] : m) {
std::cout << key << ": " << value << std::endl; // 遍历元素
}
}

5. std::set

std::set 是一种关联容器,存储唯一的键值,所有元素按照键值排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <set>
#include <iostream>

int main() {
std::set<int> s;
s.insert(1); // 插入元素
s.insert(2);
s.erase(1); // 删除元素
s.find(2); // 查找元素
s.size(); // 返回元素数量
s.clear(); // 清空所有元素
s.contains(2); // 检查元素是否存在

for (const auto& elem : s) {
std::cout << elem << " "; // 遍历元素
}
std::cout << std::endl;
}

6. std::unordered_map

std::unordered_map 是一种哈希表,存储键值对,元素无序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <unordered_map>
#include <iostream>

int main() {
std::unordered_map<int, std::string> um;
um.insert({1, "one"}); // 插入元素
um[2] = "two"; // 使用[]操作符插入或修改元素
um.erase(1); // 删除键为1的元素
um.find(2); // 查找键为2的元素
um.size(); // 返回元素数量
um.clear(); // 清空所有元素
um.contains(2); // 检查键是否存在

for (const auto& [key, value] : um) {
std::cout << key << ": " << value << std::endl; // 遍历元素
}
}

7. std::unordered_set

std::unordered_set 是一种哈希表,存储唯一的键值,元素无序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <unordered_set>
#include <iostream>

int main() {
std::unordered_set<int> us;
us.insert(1); // 插入元素
us.insert(2);
us.erase(1); // 删除元素
us.find(2); // 查找元素
us.size(); // 返回元素数量
us.clear(); // 清空所有元素
us.contains(2); // 检查元素是否存在

for (const auto& elem : us) {
std::cout << elem << " "; // 遍历元素
}
std::cout << std::endl;
}

8. std::string

std::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
#include <string>
#include <iostream>

int main() {
std::string str = "Hello, World!"; // 创建一个字符串
std::string str2("C++23"); // 使用另一种方式创建字符串

str.size(); // 返回字符串的长度
str.length(); // 返回字符串的长度
str.empty(); // 检查字符串是否为空
str.clear(); // 清空字符串

str[0]; // 访问第一个字符
str.at(1); // 访问第二个字符(带范围检查)

str.append(" Welcome!"); // 在字符串末尾添加内容
str += " Enjoy!"; // 使用+=运算符添加内容

str.insert(5, " C++"); // 在指定位置插入内容

str.erase(0, 5); // 删除从索引0开始的5个字符

str.substr(0, 5); // 提取从索引0开始的5个字符组成的子字符串

str.find("World"); // 查找子字符串的位置
str.find_first_of("aeiou"); // 查找第一个元音字母的位置

std::cout << str << std::endl; // 输出字符串
}

9. std::optional

std::optional 是 C++17 引入的一个模板,用于表示可能不存在的值。它是一个包装器,可以包含一个类型为 T 的值或者不包含任何值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <optional>
#include <iostream>

std::optional<int> get_data(bool flag) {
if (flag)
return 123; // 返回一个包含 int 的 optional
else
return {}; // 返回一个空的 optional
}

void use_optional() {
auto result = get_data(true);
if (result) {
std::cout << "值存在: " << *result << std::endl; // 输出 123
} else {
std::cout << "值不存在" << std::endl;
}
}

10. std::shared_ptr

std::shared_ptr 是 C++ 标准库中的一个智能指针,它可以用来管理具有引用计数的动态分配的对象。当 std::shared_ptr 的最后一个实例被销毁时,其指向的对象也会被自动释放。

1
2
3
4
5
6
7
8
9
10
11
#include <memory>

void example() {
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2 = p1; // 引用计数现在是2

std::cout << *p1 << std::endl; // 输出 10
std::cout << *p2 << std::endl; // 输出 10

// p1 和 p2 被销毁,对象的引用计数变为0,对象被删除
}

11. std::stringstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <sstream>
#include <vector>
#include <string>

int main() {
std::stringstream oss;
oss << "Hello, world!";

auto output = std::vector<std::string>{};
output.push_back(oss.str()); // 将 std::stringstream 的内容作为 std::string 添加到 vector 中

for (const auto& str : output) {
std::cout << str << std::endl; // 输出: Hello, world!
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::string data = "100 100 100 100 100";
std::stringstream ss(data);

int num;
std::vector<int> numbers;

while (ss >> num) {
numbers.push_back(num);
}

std::cout << "Number of integers: " << numbers.size() << std::endl;
for (int n : numbers) {
std::cout << n << " ";
}

迭代器iterator

constness-reverse.cpp

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
#include <iostream>
#include <vector>

int main()
{
std::vector<int> ages;
ages.push_back(18);
ages.push_back(19);
ages.push_back(20);

// type of iter would be std::vector<int>::iterator
for (auto iter = ages.begin(); iter != ages.end(); ++iter) {
(*iter)++; // OK
}

// type of iter would be std::vector<int>::const_iterator
for (auto iter = ages.cbegin(); iter != ages.cend(); ++iter) {
//(*iter)++; // NOT OK
}

// type of iter would be std::vector<int>::reverse_iterator
for (auto iter = ages.rbegin(); iter != ages.rend(); ++iter) {
std::cout << *iter << "\n"; // prints 20, 19, 18
}

// Can also use crbegin and crend
}

Vector、Deque 和 Array
这些序列容器提供随机访问迭代器,支持迭代器加减、比较和随机访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <vector>
#include <iostream>

int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};

// 正向遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;

// 反向遍历
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
std::cout << *rit << " ";
}
std::cout << std::endl;
}

Map 和 Set
这些关联容器提供双向迭代器。它们的迭代器不支持随机访问,但可以进行递增和递减操作。

1
2
3
4
5
6
7
8
9
10
11
#include <map>
#include <iostream>

int main() {
std::map<std::string, int> mp = {{"one", 1}, {"two", 2}, {"three", 3}};

// 遍历 map
for (auto it = mp.begin(); it != mp.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
}

Random Access Iterator

下标操作 (operator[]): 允许直接访问迭代器指向的元素的指定偏移位置的元素,就像使用数组一样。

1
2
3
std::vector<int> v = {10, 20, 30, 40, 50};
auto it = v.begin();
std::cout << it[2]; // 输出 30

迭代器加减整数:可以将迭代器加上或减去一个整数,移动到当前位置的前后指定位置。

1
2
3
std::vector<int> v = {10, 20, 30, 40, 50};
auto it = v.begin() + 3; // 指向 40
std::cout << *it; // 输出 40

两个迭代器之间的减法:计算两个迭代器之间的距离,结果是两个迭代器指向的元素之间的位置差。

1
2
3
4
std::vector<int> v = {10, 20, 30, 40, 50};
auto it1 = v.begin();
auto it2 = v.end();
std::cout << (it2 - it1); // 输出 5,表示容器中有 5 个元素

迭代器的比较:可以比较两个迭代器的位置关系。

1
2
3
4
5
6
std::vector<int> v = {10, 20, 30, 40, 50};
auto it1 = v.begin();
auto it2 = v.begin() + 2;
if (it1 < it2) {
std::cout << "it1 在 it2 之前";
}

Sample:

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
#include <iostream>
#include <vector>

int main() {
std::vector<int> vec = {5, 10, 15, 20, 25};

// 随机访问迭代器
auto it = vec.begin();

// 输出开始位置的元素
std::cout << "First element: " << *it << std::endl;

// 向前移动迭代器
it += 2;
std::cout << "Third element: " << *it << std::endl;

// 直接通过迭代器访问第四个元素
std::cout << "Fourth element: " << it[1] << std::endl;

// 计算两个迭代器之间的距离
auto it2 = vec.end();
std::cout << "Distance from start to end: " << (it2 - it) << std::endl;

return 0;
}

Class

成员访问控制 Member Access Control

This is how we support encapsulation and information hiding in C++
member-access.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class foo {
public:
// Members accessible by everyone
foo(); // The default constructor.

protected:
// Members accessible by members, friends, and subclasses
// Will discuss this when we do advanced OOP in future weeks.

private:
// Accessible only by members and friends
void private_member_function();
int private_data_member_;

public:
// May define multiple sections of the same name
};

构造函数 Constructor

constructor-basic.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

class myclass {
public:
myclass(int i) {
i_ = i;
}
int getval() {
return i_;
}

private:
int i_;
};

int main() {
auto mc = myclass { 1 };
std::cout << mc.getval() << "\n";
}

This 指针

成员函数有一个额外的隐式参数,名为 this

  • A member function has an extra implicit parameter, named this
  • This is a pointer to the object on behalf of which the function is called
  • A member function does not explicitly define it, but may explicitly use it
  • The compiler treats an unqualified reference to a class member as being made through the this pointer.
  • Generally we use a “_” suffix for class variables rather than a this-> to identify them

这是一个指向代表其调用函数的对象的指针:

  • 成员函数没有明确定义它,但可以明确使用它。
  • 编译器将对类成员的不合格引用视为通过 this 指针进行的。
  • 通常,我们使用“_”后缀来标识类变量,而不是使用 this->
  • this 是一个指向当前对象的常量指针,不能被修改。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class myclass {
public:
myclass(int i) {
// i_ = i;
this->i_ = i;
}
int getval() {
// return i_;
return this->i_;
}

private:
int i_;
};

int main() {
auto mc = myclass { 1 };
std::cout << mc.getval() << "\n";
}

explicit 显式关键字

自定义类intvec

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
#include <vector>

class intvec {
public:
// This one allows the implicit conversion
intvec(std::vector<int>::size_type length)
: vec_(length, 0) {}

// This one disallows it.
explicit intvec(std::vector<int>::size_type length)
: vec_(length, 0) {}

private:
std::vector<int> vec_;
};

auto main() -> int {
int const size = 20;
// Explicitly calling the constructor.
intvec container1{ size }; // Construction
intvec container2 = intvec{ size }; // Assignment

// Implicit conversion.
// Probably not what we want.
// intvec container3 = size; // This line would cause a compile-time error if uncommented.
}

代码解析:

  1. 类定义intvec 类中包含一个私有成员 vec_,它是一个 std::vector<int> 类型。
  2. 构造函数:该类有一个构造函数,可接受 std::vector<int>::size_type 类型的参数,用于初始化 vec_ 的大小和默认值。
  3. 显式构造函数:使用关键字 explicit 修饰构造函数,防止隐式类型转换。
  4. 主函数
    • container1 使用构造函数初始化,直接使用花括号。
    • container2 使用显式的构造函数调用进行初始化。
    • container3(已注释)如果取消注释,将尝试隐式转换,但由于构造函数被声明为 explicit,所以这将引发编译错误。

Const 成员

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
#include <iostream>
#include <string>

class person {
public:
person(std::string const& name) : name_{ name } {}

auto set_name(std::string const& name) -> void {
name_ = name;
}
auto get_name() -> std::string const& {
return name_;
}

private:
std::string name_;
};

auto main() -> int {
person p1 { "Hayden" };
p1.set_name("Chris");
std::cout << p1.get_name() << "\n";

person const p2 { "Hayden" };
// p2.set_name("Chris"); // WILL NOT WORK... WHY NOT?
// std::cout << p2.get_name() << "\n"; // WILL NOT WORK... WHY NOT?
}

静态多态(编译时多态)

静态多态通常是通过函数重载运算符重载实现的。编译器在编译时期决定了使用哪个函数,这种决策是基于参数的数量和类型。

1
2
3
4
5
6
7
8
9
class Print {
public:
void display(int i) {
std::cout << "整数:" << i << std::endl;
}
void display(double f) {
std::cout << "浮点数:" << f << std::endl;
}
};

动态多态(运行时多态)

动态多态是通过虚函数实现的。当一个类声明了虚函数,它允许派生类重写该函数。具体调用哪个函数是在程序运行时决定的,基于对象的实际类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Base {
public:
virtual void print() {
std::cout << "这是基类的函数。" << std::endl;
}
};

class Derived : public Base {
public:
void print() override { // 重写基类的函数
std::cout << "这是派生类的函数。" << std::endl;
}
};

void function(Base& b) {
b.print(); // 运行时确定调用哪个函数
}

Templates 模板

1
2
3
4
5
6
7
8
9
10
11
12
template <typename T>
T add(T a, T b) {
return a + b;
}

std::remove_cvref_t<decltype(add(2, 3))> sum1 = add(2, 3); // 调用 int 版本
std::forward<decltype(add(2.5, 3.5))> sum2 = add(2.5, 3.5); // 调用 double 版本

int main() {
int sum1 = add<int>(2, 3); // 调用 int 版本
double sum2 = add<double>(2.5, 3.5); // 调用 double 版本
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <array>

template <typename T, int size>
T findmin(const std::array<T, size> a) {
T min = a[0];
for (int i = 1; i < size; ++i) {
if (a[i] < min) min = a[i];
}
return min;
}

int main() {
std::array<int, 3> x{ 3, 1, 2 };
std::array<double, 4> y{ 3.3, 1.1, 2.2, 4.4 };
std::cout << "x 的最小值 = " << findmin(x) << "\\n";
std::cout << "y 的最小值 = " << findmin(y) << "\\n";
}

database.cpp

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
#ifndef DATABASE_HPP
#define DATABASE_HPP

#include "record.hpp"
#include "query.hpp"
#include <algorithm>
#include <ostream>
#include <map>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
namespace q2 {
class database {
public:
database() = default;
~database() = default;

auto insert(record const& r) -> void {
records_.push_back(r);
}

auto count() const -> std::size_t {
return records_.size();
}

friend auto operator<<(std::ostream& os, database const& g) -> std::ostream& {
for(auto it =g.records_.begin(); it != g.records_.end(); ++it){
os << (*it);
}
return os;
}
friend auto operator>>(std::istream& is, database& sm) -> std::istream& {
auto r = record();
while(is >> r){
sm.insert(r);
r = record();
}
return is;
}
auto delete_matching(query const& q) -> std::size_t {
return std::erase_if(records_, [&q](auto const& item){
return q.matches(item);
});
}
auto select(query const& q) const -> database {
database n;
std::for_each(records_.begin(), records_.end(), [&q, &n](auto const& item){
if(q.matches(item)){
n.insert(item);
}
});
return n;
}
private:
std::vector<record> records_;
};

} // namespace q2

#endif // DATABASE_HPP

query.hpp

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#ifndef QUERY_HPP
#define QUERY_HPP

#include <algorithm>
#include <ostream>
#include <map>
#include <iostream>
#include <regex>
#include <string>
#include <sstream>
#include "record.hpp"

namespace q2 {
class record;

class query {
public:
virtual ~query() = default;
virtual auto clone() const -> query* {
return new query(*this);
}
auto virtual matches(record const&) const -> bool{
return true;
};
};

class query_equals : public query {
public:
query_equals(std::string attr, std::string value):
attr_(attr),
value_(value){};

auto matches(const record& r) const -> bool override {
if (r.has_attribute(attr_)){
return r.get_value(attr_) == value_;
}
return false;
}
auto clone() const -> query_equals* override {
return new query_equals(attr_, value_);
}
private:
std::string attr_;
std::string value_;
};

// class query_less_than
class query_less_than : public query {
public:
query_less_than(std::string attr, std::string value):
attr_(attr),
value_(value){};

auto matches(const record& r) const -> bool override {
if (r.has_attribute(attr_)){
return r.get_value(attr_) < value_;
}
return false;
}
auto clone() const -> query_less_than* override {
return new query_less_than(attr_, value_);
}
private:
std::string attr_;
std::string value_;
};


// class query_greater_than
class query_greater_than : public query {
public:
query_greater_than(std::string attr, std::string value):
attr_(attr),
value_(value){};

auto matches(const record& r) const -> bool override {
if (r.has_attribute(attr_)){
return r.get_value(attr_) > value_;
}
return false;
}
auto clone() const -> query_greater_than* override {
return new query_greater_than(attr_, value_);
}
private:
std::string attr_;
std::string value_;
};

// class query_starts_with
class query_starts_with : public query {
public:
query_starts_with(std::string attr, std::string value):
attr_(attr),
value_(value){};

auto matches(const record& r) const -> bool override {
if (r.has_attribute(attr_)){
auto s = r.get_value(attr_);
auto reg = std::regex("^"+value_);
if(std::regex_search(s, reg)){
return true;
}
}
return false;
}
auto clone() const -> query_starts_with* override {
return new query_starts_with(attr_, value_);
}
private:
std::string attr_;
std::string value_;
};

// class query_and
class query_and : public query{
public:
query_and(query const& q1, query const& q2):
q1_(q1.clone()), q2_(q2.clone()){};
auto matches(const record& r) const -> bool override{
return q1_->matches(r) and q2_->matches(r);
}

auto clone() const -> query_and* override{
return new query_and(*q1_, *q2_);
}

~query_and(){
delete q1_;
delete q2_;
}

private:
query* q1_;
query* q2_;
};

// class query_or
class query_or : public query{
public:
query_or(query const& q1, query const& q2):
q1_(q1.clone()), q2_(q2.clone()){};
auto matches(const record& r) const -> bool override{
return q1_->matches(r) or q2_->matches(r);
}

auto clone() const -> query_or* override{
return new query_or(*q1_, *q2_);
}

~query_or(){
delete q1_;
delete q2_;
}

private:
query* q1_;
query* q2_;
};

// class query_not
class query_not : public query{
public:
query_not(query const& q1):
q1_(q1.clone()){};
auto matches(const record& r) const -> bool override{
return !q1_->matches(r);
}

auto clone() const -> query_not* override{
return new query_not(*q1_);
}

~query_not(){
delete q1_;
}

private:
query* q1_;
};
} // namespace q2

#endif // QUERY_HPP

record.hpp

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
#ifndef RECORD_HPP
#define RECORD_HPP

#include <algorithm>
#include <ostream>
#include <map>
#include <iostream>
#include <string>
#include <sstream>

namespace q2 {
class record {
public:
record() {};
~record() {};
auto get_value(std::string const& s) const -> std::string {
auto iter = values_.find(s);
if(iter != values_.end()){
return iter->second;
}
return "";
}

auto has_attribute(std::string const& key) const -> bool {
auto iter = values_.find(key);
if(iter != values_.end()){
return true;
}
return false;
}
auto count() const -> std::size_t {
return values_.size();
}

auto delete_attribute(std::string const& key) -> bool {
if(has_attribute(key)){
values_.erase(key);
return true;
}
return false;
}
auto set_value(std::string const& key, std::string const& val) -> void {
values_[key] = val;
}
friend auto operator<<(std::ostream& os, record const& g) -> std::ostream& {
os << "{\n";
std::for_each(g.values_.begin(), g.values_.end(), [&os](auto const& iter){
os << string_replace(string_replace(iter.first, "!", "!!"), "=", "!=") << "=" << string_replace(string_replace(iter.second, "!", "!!"), "=", "!=") << std::endl;
});
os << "}" << std::endl;
return os;
}
friend auto operator>>(std::istream& is, record& sm) -> std::istream& {
(void) sm;
for(std::string s; std::getline(is, s);){
if (s == "{"){
continue;
}
if (s == "}"){
break;
}
s = string_replace(s, "!!", "!");
auto p = s.find("=");
auto pp = s.find("!=");
while(pp == p-1){
p = s.find("=", p+1);
pp = s.find("!=", p+1);
}

sm.set_value(string_replace(s.substr(0, p), "!=", "="), string_replace(s.substr(p+1, s.size()), "!=", "="));
}

return is;
}
private:
std::map<std::string, std::string> values_;
static auto string_replace(std::string const& s, std::string const& target, std::string const& repl) -> std::string{
auto ret = s;
auto pos = ret.find(target);
while(pos != std::string::npos){
ret.replace(pos, target.size(), repl);
pos = ret.find(target, pos+repl.size());
}
return ret;
}
};
} // namespace q2

#endif // RECORD_HPP