mirror of
https://github.com/changkun/modern-cpp-tutorial.git
synced 2025-12-16 20:27:08 +03:00
book: formatting style updates (#231)
This commit is contained in:
@@ -214,7 +214,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// should output: 1, 4, 3, 4. can be simplified using `auto`
|
// should output: 1, 4, 3, 4. can be simplified using `auto`
|
||||||
for (std::vector<int>::iterator element = vec.begin(); element != vec.end(); ++element)
|
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
|
||||||
|
++element)
|
||||||
std::cout << *element << std::endl;
|
std::cout << *element << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -301,7 +302,8 @@ int main() {
|
|||||||
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
std::cout << "magicFoo: ";
|
std::cout << "magicFoo: ";
|
||||||
for (std::vector<int>::iterator it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it)
|
for (std::vector<int>::iterator it = magicFoo.vec.begin();
|
||||||
|
it != magicFoo.vec.end(); ++it)
|
||||||
std::cout << *it << std::endl;
|
std::cout << *it << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -221,8 +221,8 @@ int foo(int a, int b, int c) {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// bind parameter 1, 2 on function foo, and use std::placeholders::_1 as placeholder
|
// bind parameter 1, 2 on function foo,
|
||||||
// for the first parameter.
|
// and use std::placeholders::_1 as placeholder for the first parameter.
|
||||||
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
||||||
// when call bindFoo, we only need one param left
|
// when call bindFoo, we only need one param left
|
||||||
bindFoo(1);
|
bindFoo(1);
|
||||||
@@ -355,7 +355,8 @@ int main()
|
|||||||
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
std::string&& rv1 = std::move(lv1); // legal, std::move can convert lvalue to rvalue
|
||||||
std::cout << rv1 << std::endl; // string,
|
std::cout << rv1 << std::endl; // string,
|
||||||
|
|
||||||
const std::string& lv2 = lv1 + lv1; // legal, const lvalue reference can extend temp variable's lifecycle
|
const std::string& lv2 = lv1 + lv1; // legal, const lvalue reference can
|
||||||
|
// extend temp variable's lifecycle
|
||||||
// lv2 += "Test"; // illegal, const ref can't be modified
|
// lv2 += "Test"; // illegal, const ref can't be modified
|
||||||
std::cout << lv2 << std::endl; // string,string,
|
std::cout << lv2 << std::endl; // string,string,
|
||||||
|
|
||||||
@@ -482,8 +483,9 @@ int main() {
|
|||||||
// "str: Hello world."
|
// "str: Hello world."
|
||||||
std::cout << "str: " << str << std::endl;
|
std::cout << "str: " << str << std::endl;
|
||||||
|
|
||||||
// use push_back(const T&&), no copy
|
// use push_back(const T&&),
|
||||||
// the string will be moved to vector, and therefore std::move can reduce copy cost
|
// no copy the string will be moved to vector,
|
||||||
|
// and therefore std::move can reduce copy cost
|
||||||
v.push_back(std::move(str));
|
v.push_back(std::move(str));
|
||||||
// str is empty now
|
// str is empty now
|
||||||
std::cout << "str: " << str << std::endl;
|
std::cout << "str: " << str << std::endl;
|
||||||
|
|||||||
@@ -57,27 +57,27 @@ And see the reference count of an object by `use_count()`. E.g:
|
|||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
auto pointer2 = pointer; // reference count+1
|
auto pointer2 = pointer; // reference count+1
|
||||||
auto pointer3 = pointer; // reference count+1
|
auto pointer3 = pointer; // reference count+1
|
||||||
int *p = pointer.get(); // no increase of reference count
|
int *p = pointer.get(); // no increase of reference count
|
||||||
|
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
||||||
|
|
||||||
pointer2.reset();
|
pointer2.reset();
|
||||||
std::cout << "reset pointer2:" << std::endl;
|
std::cout << "reset pointer2:" << std::endl;
|
||||||
|
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
||||||
std::cout << "pointer2.use_count() = "
|
std::cout << "pointer2.use_count() = "
|
||||||
<< pointer2.use_count() << std::endl; // 0, pointer2 has reset
|
<< pointer2.use_count() << std::endl; // pointer2 has reset, 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
||||||
|
|
||||||
pointer3.reset();
|
pointer3.reset();
|
||||||
std::cout << "reset pointer3:" << std::endl;
|
std::cout << "reset pointer3:" << std::endl;
|
||||||
|
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
||||||
std::cout << "pointer3.use_count() = "
|
std::cout << "pointer3.use_count() = "
|
||||||
<< pointer3.use_count() << std::endl; // 0, pointer3 has reset
|
<< pointer3.use_count() << std::endl; // pointer3 has reset, 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5.3 `std::unique_ptr`
|
## 5.3 `std::unique_ptr`
|
||||||
@@ -85,7 +85,7 @@ std::cout << "pointer3.use_count() = "
|
|||||||
`std::unique_ptr` is an exclusive smart pointer that prohibits other smart pointers from sharing the same object, thus keeping the code safe:
|
`std::unique_ptr` is an exclusive smart pointer that prohibits other smart pointers from sharing the same object, thus keeping the code safe:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique was introduced in C++14
|
std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique, from C++14
|
||||||
std::unique_ptr<int> pointer2 = pointer; // illegal
|
std::unique_ptr<int> pointer2 = pointer; // illegal
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -84,8 +84,9 @@ We use a simple example to briefly introduce the use of this library. Consider t
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
||||||
// In C++, `\` will be used as an escape character in the string. In order for `\.`
|
// In C++, `\` will be used as an escape character in the string.
|
||||||
// to be passed as a regular expression, it is necessary to perform second escaping of `\`, thus we have `\\.`
|
// In order for `\.` to be passed as a regular expression,
|
||||||
|
// it is necessary to perform second escaping of `\`, thus we have `\\.`
|
||||||
std::regex txt_regex("[a-z]+\\.txt");
|
std::regex txt_regex("[a-z]+\\.txt");
|
||||||
for (const auto &fname: fnames)
|
for (const auto &fname: fnames)
|
||||||
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
||||||
@@ -104,7 +105,8 @@ std::smatch base_match;
|
|||||||
for(const auto &fname: fnames) {
|
for(const auto &fname: fnames) {
|
||||||
if (std::regex_match(fname, base_match, base_regex)) {
|
if (std::regex_match(fname, base_match, base_regex)) {
|
||||||
// the first element of std::smatch matches the entire string
|
// the first element of std::smatch matches the entire string
|
||||||
// the second element of std::smatch matches the first expression with brackets
|
// the second element of std::smatch matches the first expression
|
||||||
|
// with brackets
|
||||||
if (base_match.size() == 2) {
|
if (base_match.size() == 2) {
|
||||||
std::string base = base_match[1].str();
|
std::string base = base_match[1].str();
|
||||||
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
|
std::cout << "sub-match[0]: " << base_match[0].str() << std::endl;
|
||||||
@@ -187,32 +189,36 @@ Please implement the member functions `start()` and `parse_request`. Enable serv
|
|||||||
template<typename SERVER_TYPE>
|
template<typename SERVER_TYPE>
|
||||||
void start_server(SERVER_TYPE &server) {
|
void start_server(SERVER_TYPE &server) {
|
||||||
|
|
||||||
// process GET request for /match/[digit+numbers], e.g.
|
// process GET request for /match/[digit+numbers],
|
||||||
// GET request is /match/abc123, will return abc123
|
// e.g. GET request is /match/abc123, will return abc123
|
||||||
server.resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
server.resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string number=request.path_match[1];
|
string number=request.path_match[1];
|
||||||
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length()
|
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length()
|
||||||
<< "\r\n\r\n" << number;
|
<< "\r\n\r\n" << number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// peocess default GET request; anonymous function will be called if no other matches
|
// peocess default GET request;
|
||||||
// response files in folder web/
|
// anonymous function will be called
|
||||||
|
// if no other matches response files in folder web/
|
||||||
// default: index.html
|
// default: index.html
|
||||||
server.default_resource["fill_your_reg_ex"]["GET"] =
|
server.default_resource["fill_your_reg_ex"]["GET"] =
|
||||||
[](ostream& response, Request& request) {
|
[](ostream& response, Request& request)
|
||||||
string filename = "www/";
|
{
|
||||||
|
string filename = "www/";
|
||||||
|
|
||||||
string path = request.path_match[1];
|
string path = request.path_match[1];
|
||||||
|
|
||||||
// forbidden use `..` access content outside folder web/
|
// forbidden use `..` access content outside folder web/
|
||||||
size_t last_pos = path.rfind(".");
|
size_t last_pos = path.rfind(".");
|
||||||
size_t current_pos = 0;
|
size_t current_pos = 0;
|
||||||
size_t pos;
|
size_t pos;
|
||||||
while((pos=path.find('.', current_pos)) != string::npos && pos != last_pos) {
|
while((pos=path.find('.', current_pos)) != string::npos && pos != last_pos) {
|
||||||
current_pos = pos;
|
current_pos = pos;
|
||||||
path.erase(pos, 1);
|
path.erase(pos, 1);
|
||||||
last_pos--;
|
last_pos--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (...)
|
// (...)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ int main() {
|
|||||||
std::cout << "waiting...";
|
std::cout << "waiting...";
|
||||||
result.wait(); // block until future has arrived
|
result.wait(); // block until future has arrived
|
||||||
// output result
|
// output result
|
||||||
std::cout << "done!" << std:: endl << "future result is " << result.get() << std::endl;
|
std::cout << "done!" << std:: endl << "future result is "
|
||||||
|
<< result.get() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -196,7 +197,8 @@ int main() {
|
|||||||
// temporal unlock to allow producer produces more rather than
|
// temporal unlock to allow producer produces more rather than
|
||||||
// let consumer hold the lock until its consumed.
|
// let consumer hold the lock until its consumed.
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // consumer is slower
|
// consumer is slower
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
lock.lock();
|
lock.lock();
|
||||||
if (!produced_nums.empty()) {
|
if (!produced_nums.empty()) {
|
||||||
std::cout << "consuming " << produced_nums.front() << std::endl;
|
std::cout << "consuming " << produced_nums.front() << std::endl;
|
||||||
@@ -272,7 +274,7 @@ This is a very strong set of synchronization conditions, in other words when it
|
|||||||
This seems too harsh for a variable that requires only atomic operations (no intermediate state).
|
This seems too harsh for a variable that requires only atomic operations (no intermediate state).
|
||||||
|
|
||||||
The research on synchronization conditions has a very long history, and we will not go into details here. Readers should understand that under the modern CPU architecture, atomic operations at the CPU instruction level are provided.
|
The research on synchronization conditions has a very long history, and we will not go into details here. Readers should understand that under the modern CPU architecture, atomic operations at the CPU instruction level are provided.
|
||||||
Therefore, in the C++11 multi-threaded shared variable reading and writing, the introduction of the `std::atomic` template, so that we instantiate an atomic type, will be a
|
Therefore, in the C++11 multi-threaded shared variable reading and writing, the introduction of the `std::atomic` template, so that we instantiate an atomic type, will be an
|
||||||
Atomic type read and write operations are minimized from a set of instructions to a single CPU instruction. E.g:
|
Atomic type read and write operations are minimized from a set of instructions to a single CPU instruction. E.g:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -417,8 +419,11 @@ Weakening the synchronization conditions between processes, usually we will cons
|
|||||||
```
|
```
|
||||||
3 4 4 4 // The write operation of x was quickly observed
|
3 4 4 4 // The write operation of x was quickly observed
|
||||||
0 3 3 4 // There is a delay in the observed time of the x write operation
|
0 3 3 4 // There is a delay in the observed time of the x write operation
|
||||||
0 0 0 4 // The last read read the final value of x, but the previous changes were not observed.
|
0 0 0 4 // The last read read the final value of x,
|
||||||
0 0 0 0 // The write operation of x is not observed in the current time period, but the situation that x is 4 can be observed at some point in the future.
|
// but the previous changes were not observed.
|
||||||
|
0 0 0 0 // The write operation of x is not observed in the current time period,
|
||||||
|
// but the situation that x is 4 can be observed
|
||||||
|
// at some point in the future.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Memory Orders
|
### Memory Orders
|
||||||
@@ -480,9 +485,8 @@ To achieve the ultimate performance and achieve consistency of various strength
|
|||||||
});
|
});
|
||||||
std::thread acqrel([&]() {
|
std::thread acqrel([&]() {
|
||||||
int expected = 1; // must before compare_exchange_strong
|
int expected = 1; // must before compare_exchange_strong
|
||||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
|
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel))
|
||||||
expected = 1; // must after compare_exchange_strong
|
expected = 1; // must after compare_exchange_strong
|
||||||
}
|
|
||||||
// flag has changed to 2
|
// flag has changed to 2
|
||||||
});
|
});
|
||||||
std::thread acquire([&]() {
|
std::thread acquire([&]() {
|
||||||
|
|||||||
@@ -176,7 +176,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 将输出 1, 4, 3, 4
|
// 将输出 1, 4, 3, 4
|
||||||
for (std::vector<int>::iterator element = vec.begin(); element != vec.end(); ++element)
|
for (std::vector<int>::iterator element = vec.begin(); element != vec.end();
|
||||||
|
++element)
|
||||||
std::cout << *element << std::endl;
|
std::cout << *element << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -228,7 +229,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
为了解决这个问题,C++11 首先把初始化列表的概念绑定到了类型上,并将其称之为 `std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
|
为解决这个问题,C++11 首先把初始化列表的概念绑定到类型上,称其为 `std::initializer_list`,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
@@ -249,7 +250,9 @@ int main() {
|
|||||||
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
MagicFoo magicFoo = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
std::cout << "magicFoo: ";
|
std::cout << "magicFoo: ";
|
||||||
for (std::vector<int>::iterator it = magicFoo.vec.begin(); it != magicFoo.vec.end(); ++it) std::cout << *it << std::endl;
|
for (std::vector<int>::iterator it = magicFoo.vec.begin();
|
||||||
|
it != magicFoo.vec.end(); ++it)
|
||||||
|
std::cout << *it << std::endl;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ Lambda 表达式的本质是一个和函数对象类型相似的类类型(称
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using foo = void(int); // 定义函数类型, using 的使用见上一节中的别名语法
|
using foo = void(int); // 定义函数类型, using 的使用见上一节中的别名语法
|
||||||
void functional(foo f) { // 定义在参数列表中的函数类型 foo 被视为退化后的函数指针类型 foo*
|
void functional(foo f) { // 参数列表中定义的函数类型 foo 被视为退化后的函数指针类型 foo*
|
||||||
f(1); // 通过函数指针调用函数
|
f(1); // 通过函数指针调用函数
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,8 @@ int foo(int a, int b, int c) {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
int main() {
|
int main() {
|
||||||
// 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位
|
// 将参数1,2绑定到函数 foo 上,
|
||||||
|
// 但使用 std::placeholders::_1 来对第一个参数进行占位
|
||||||
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2);
|
||||||
// 这时调用 bindFoo 时,只需要提供第一个参数即可
|
// 这时调用 bindFoo 时,只需要提供第一个参数即可
|
||||||
bindFoo(1);
|
bindFoo(1);
|
||||||
@@ -551,7 +552,7 @@ constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcep
|
|||||||
```
|
```
|
||||||
|
|
||||||
在这份实现中,`std::remove_reference` 的功能是消除类型中的引用,
|
在这份实现中,`std::remove_reference` 的功能是消除类型中的引用,
|
||||||
而 `std::is_lvalue_reference` 用于检查类型推导是否正确,在 `std::forward` 的第二个实现中
|
`std::is_lvalue_reference` 则用于检查类型推导是否正确,在 `std::forward` 的第二个实现中
|
||||||
检查了接收到的值确实是一个左值,进而体现了坍缩规则。
|
检查了接收到的值确实是一个左值,进而体现了坍缩规则。
|
||||||
|
|
||||||
当 `std::forward` 接受左值时,`_Tp` 被推导为左值,所以返回值为左值;而当其接受右值时,
|
当 `std::forward` 接受左值时,`_Tp` 被推导为左值,所以返回值为左值;而当其接受右值时,
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ order: 5
|
|||||||
也就是我们常说的 RAII 资源获取即初始化技术。
|
也就是我们常说的 RAII 资源获取即初始化技术。
|
||||||
|
|
||||||
凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 `new` 和 `delete` 去
|
凡事都有例外,我们总会有需要将对象在自由存储上分配的需求,在传统 C++ 里我们只好使用 `new` 和 `delete` 去
|
||||||
『记得』对资源进行释放。而 C++11 引入智能指针的概念,使用引用计数的想法,让程序员不再需要关心手动释放内存。
|
『记得』对资源进行释放。而 C++11 引入了智能指针的概念,使用了引用计数的想法,让程序员不再需要关心手动释放内存。
|
||||||
这些智能指针就包括 `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`,使用它们需要包含头文件 `<memory>`。
|
这些智能指针包括 `std::shared_ptr`/`std::unique_ptr`/`std::weak_ptr`,使用它们需要包含头文件 `<memory>`。
|
||||||
|
|
||||||
> 注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,
|
> 注意:引用计数不是垃圾回收,引用计数能够尽快收回不再被使用的对象,同时在回收的过程中也不会造成长时间的等待,
|
||||||
> 更能够清晰明确的表明资源的生命周期。
|
> 更能够清晰明确的表明资源的生命周期。
|
||||||
|
|
||||||
## 5.2 `std::shared_ptr`
|
## 5.2 `std::shared_ptr`
|
||||||
|
|
||||||
`std::shared_ptr` 是一种智能指针,它能够记录多少个 `shared_ptr` 共同指向一个对象,从而消除显式的调用
|
`std::shared_ptr` 是一种智能指针,它能够记录多少个 `shared_ptr` 共同指向一个对象,从而消除显式的调用
|
||||||
`delete`,当引用计数变为零的时候就会将对象自动删除。
|
`delete`,当引用计数变为零的时候就会将对象自动删除。
|
||||||
|
|
||||||
但还不够,因为使用 `std::shared_ptr` 仍然需要使用 `new` 来调用,这使得代码出现了某种程度上的不对称。
|
但还不够,因为使用 `std::shared_ptr` 仍然需要使用 `new` 来调用,这使得代码出现了某种程度上的不对称。
|
||||||
@@ -59,21 +59,23 @@ int main() {
|
|||||||
auto pointer = std::make_shared<int>(10);
|
auto pointer = std::make_shared<int>(10);
|
||||||
auto pointer2 = pointer; // 引用计数+1
|
auto pointer2 = pointer; // 引用计数+1
|
||||||
auto pointer3 = pointer; // 引用计数+1
|
auto pointer3 = pointer; // 引用计数+1
|
||||||
int *p = pointer.get(); // 这样不会增加引用计数
|
int *p = pointer.get(); // 这样不会增加引用计数
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3
|
||||||
|
|
||||||
pointer2.reset();
|
pointer2.reset();
|
||||||
std::cout << "reset pointer2:" << std::endl;
|
std::cout << "reset pointer2:" << std::endl;
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset
|
std::cout << "pointer2.use_count() = "
|
||||||
|
<< pointer2.use_count() << std::endl; // pointer2 已 reset; 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
|
||||||
pointer3.reset();
|
pointer3.reset();
|
||||||
std::cout << "reset pointer3:" << std::endl;
|
std::cout << "reset pointer3:" << std::endl;
|
||||||
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
|
||||||
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
|
||||||
std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset
|
std::cout << "pointer3.use_count() = "
|
||||||
|
<< pointer3.use_count() << std::endl; // pointer3 已 reset; 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5.3 `std::unique_ptr`
|
## 5.3 `std::unique_ptr`
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ C++11 提供的正则表达式库操作 `std::string` 对象,
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"};
|
||||||
// 在 C++ 中 \ 会被作为字符串内的转义符,为使 \. 作为正则表达式传递进去生效,需要对 \ 进行二次转义,从而有 \\.
|
// 在 C++ 中 \ 会被作为字符串内的转义符,
|
||||||
|
// 为使 \. 作为正则表达式传递进去生效,需要对 \ 进行二次转义,从而有 \\.
|
||||||
std::regex txt_regex("[a-z]+\\.txt");
|
std::regex txt_regex("[a-z]+\\.txt");
|
||||||
for (const auto &fname: fnames)
|
for (const auto &fname: fnames)
|
||||||
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl;
|
||||||
@@ -103,7 +104,7 @@ int main() {
|
|||||||
|
|
||||||
另一种常用的形式就是依次传入 `std::string`/`std::smatch`/`std::regex` 三个参数,
|
另一种常用的形式就是依次传入 `std::string`/`std::smatch`/`std::regex` 三个参数,
|
||||||
其中 `std::smatch` 的本质其实是 `std::match_results`。
|
其中 `std::smatch` 的本质其实是 `std::match_results`。
|
||||||
在标准库中, `std::smatch` 被定义为了 `std::match_results<std::string::const_iterator>`,
|
故而在标准库的实现中, `std::smatch` 被定义为了 `std::match_results<std::string::const_iterator>`,
|
||||||
也就是一个子串迭代器类型的 `match_results`。
|
也就是一个子串迭代器类型的 `match_results`。
|
||||||
使用 `std::smatch` 可以方便的对匹配的结果进行获取,例如:
|
使用 `std::smatch` 可以方便的对匹配的结果进行获取,例如:
|
||||||
|
|
||||||
@@ -193,16 +194,23 @@ protected:
|
|||||||
template<typename SERVER_TYPE>
|
template<typename SERVER_TYPE>
|
||||||
void start_server(SERVER_TYPE &server) {
|
void start_server(SERVER_TYPE &server) {
|
||||||
|
|
||||||
// process GET request for /match/[digit+numbers], e.g. GET request is /match/abc123, will return abc123
|
// process GET request for /match/[digit+numbers],
|
||||||
server.resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
// e.g. GET request is /match/abc123, will return abc123
|
||||||
|
server.resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string number=request.path_match[1];
|
string number=request.path_match[1];
|
||||||
response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number;
|
response << "HTTP/1.1 200 OK\r\nContent-Length: "
|
||||||
|
<< number.length() << "\r\n\r\n" << number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// peocess default GET request; anonymous function will be called if no other matches
|
// peocess default GET request;
|
||||||
// response files in folder web/
|
// anonymous function will be called
|
||||||
|
// if no other matches response files in folder web/
|
||||||
// default: index.html
|
// default: index.html
|
||||||
server.default_resource["fill_your_reg_ex"]["GET"] = [](ostream& response, Request& request) {
|
server.default_resource["fill_your_reg_ex"]["GET"] =
|
||||||
|
[](ostream& response, Request& request)
|
||||||
|
{
|
||||||
string filename = "www/";
|
string filename = "www/";
|
||||||
|
|
||||||
string path = request.path_match[1];
|
string path = request.path_match[1];
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ int main() {
|
|||||||
由于 C++ 保证了所有栈对象在生命周期结束时会被销毁,所以这样的代码也是异常安全的。
|
由于 C++ 保证了所有栈对象在生命周期结束时会被销毁,所以这样的代码也是异常安全的。
|
||||||
无论 `critical_section()` 正常返回、还是在中途抛出异常,都会引发堆栈回退,也就自动调用了 `unlock()`。
|
无论 `critical_section()` 正常返回、还是在中途抛出异常,都会引发堆栈回退,也就自动调用了 `unlock()`。
|
||||||
|
|
||||||
而 `std::unique_lock` 则相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
而 `std::unique_lock` 则是相对于 `std::lock_guard` 出现的,`std::unique_lock` 更加灵活,
|
||||||
`std::unique_lock` 的对象会以独占所有权(没有其他的 `unique_lock` 对象同时拥有某个 `mutex` 对象的所有权)
|
`std::unique_lock` 的对象会以独占所有权(没有其他的 `unique_lock` 对象同时拥有某个 `mutex` 对象的所有权)
|
||||||
的方式管理 `mutex` 对象上的上锁和解锁的操作。所以在并发编程中,推荐使用 `std::unique_lock`。
|
的方式管理 `mutex` 对象上的上锁和解锁的操作。所以在并发编程中,推荐使用 `std::unique_lock`。
|
||||||
|
|
||||||
@@ -147,7 +147,8 @@ int main() {
|
|||||||
std::cout << "waiting...";
|
std::cout << "waiting...";
|
||||||
result.wait(); // 在此设置屏障,阻塞到期物的完成
|
result.wait(); // 在此设置屏障,阻塞到期物的完成
|
||||||
// 输出执行结果
|
// 输出执行结果
|
||||||
std::cout << "done!" << std:: endl << "future result is " << result.get() << std::endl;
|
std::cout << "done!" << std:: endl << "future result is "
|
||||||
|
<< result.get() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -198,7 +199,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
// 短暂取消锁,使得生产者有机会在消费者消费空前继续生产
|
// 短暂取消锁,使得生产者有机会在消费者消费空前继续生产
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 消费者慢于生产者
|
// 消费者慢于生产者
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
lock.lock();
|
lock.lock();
|
||||||
while (!produced_nums.empty()) {
|
while (!produced_nums.empty()) {
|
||||||
std::cout << "consuming " << produced_nums.front() << std::endl;
|
std::cout << "consuming " << produced_nums.front() << std::endl;
|
||||||
@@ -277,7 +279,7 @@ int main() {
|
|||||||
这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之后再来看如何实现一个简单的互斥锁)。
|
这是一组非常强的同步条件,换句话说当最终编译为 CPU 指令时会表现为非常多的指令(我们之后再来看如何实现一个简单的互斥锁)。
|
||||||
这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻了。
|
这对于一个仅需原子级操作(没有中间态)的变量,似乎太苛刻了。
|
||||||
|
|
||||||
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,在现代 CPU 体系结构下提供了 CPU 指令级的原子操作,
|
关于同步条件的研究有着非常久远的历史,我们在这里不进行赘述。读者应该明白,现代 CPU 体系结构提供了 CPU 指令级的原子操作,
|
||||||
因此在 C++11 中多线程下共享变量的读写这一问题上,还引入了 `std::atomic` 模板,使得我们实例化一个原子类型,将一个
|
因此在 C++11 中多线程下共享变量的读写这一问题上,还引入了 `std::atomic` 模板,使得我们实例化一个原子类型,将一个
|
||||||
原子类型读写操作从一组指令,最小化到单个 CPU 指令。例如:
|
原子类型读写操作从一组指令,最小化到单个 CPU 指令。例如:
|
||||||
|
|
||||||
@@ -311,7 +313,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于 CPU 的架构以及所实例化的类型结构是否满足该架构对内存对齐
|
当然,并非所有的类型都能提供原子操作,这是因为原子操作的可行性取决于具体的 CPU 架构,以及所实例化的类型结构是否能够满足该 CPU 架构对内存对齐
|
||||||
条件的要求,因而我们总是可以通过 `std::atomic<T>::is_lock_free` 来检查该原子类型是否需支持原子操作,例如:
|
条件的要求,因而我们总是可以通过 `std::atomic<T>::is_lock_free` 来检查该原子类型是否需支持原子操作,例如:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@@ -419,7 +421,7 @@ int main() {
|
|||||||
|
|
||||||
|
|
||||||
T2 ---------+------------+--------------------+--------+-------->
|
T2 ---------+------------+--------------------+--------+-------->
|
||||||
x.read() x.read() x.read() x.read()
|
x.read x.read() x.read() x.read()
|
||||||
```
|
```
|
||||||
|
|
||||||
在上面的情况中,如果我们假设 x 的初始值为 0,则 `T2` 中四次 `x.read()` 结果可能但不限于以下情况:
|
在上面的情况中,如果我们假设 x 的初始值为 0,则 `T2` 中四次 `x.read()` 结果可能但不限于以下情况:
|
||||||
@@ -428,7 +430,8 @@ int main() {
|
|||||||
3 4 4 4 // x 的写操作被很快观察到
|
3 4 4 4 // x 的写操作被很快观察到
|
||||||
0 3 3 4 // x 的写操作被观察到的时间存在一定延迟
|
0 3 3 4 // x 的写操作被观察到的时间存在一定延迟
|
||||||
0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到
|
0 0 0 4 // 最后一次读操作读到了 x 的最终值,但此前的变化并未观察到
|
||||||
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,但未来某个时间点上一定能观察到 x 为 4 的情况
|
0 0 0 0 // 在当前时间段内 x 的写操作均未被观察到,
|
||||||
|
// 但未来某个时间点上一定能观察到 x 为 4 的情况
|
||||||
```
|
```
|
||||||
|
|
||||||
### 内存顺序
|
### 内存顺序
|
||||||
@@ -489,9 +492,8 @@ int main() {
|
|||||||
});
|
});
|
||||||
std::thread acqrel([&]() {
|
std::thread acqrel([&]() {
|
||||||
int expected = 1; // must before compare_exchange_strong
|
int expected = 1; // must before compare_exchange_strong
|
||||||
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel)) {
|
while(!flag.compare_exchange_strong(expected, 2, std::memory_order_acq_rel))
|
||||||
expected = 1; // must after compare_exchange_strong
|
expected = 1; // must after compare_exchange_strong
|
||||||
}
|
|
||||||
// flag has changed to 2
|
// flag has changed to 2
|
||||||
});
|
});
|
||||||
std::thread acquire([&]() {
|
std::thread acquire([&]() {
|
||||||
@@ -553,7 +555,7 @@ C++11 语言层提供了并发编程的相关支持,本节简单的介绍了 `
|
|||||||
|
|
||||||
## 进一步阅读的参考资料
|
## 进一步阅读的参考资料
|
||||||
|
|
||||||
- [C++ 并发编程\(中文版\)](https://www.gitbook.com/book/chenxiaowei/cpp_concurrency_in_action/details)
|
- [C++ 并发编程\(中文版\)](https://book.douban.com/subject/26386925/)
|
||||||
- [线程支持库文档](https://en.cppreference.com/w/cpp/thread)
|
- [线程支持库文档](https://en.cppreference.com/w/cpp/thread)
|
||||||
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
- Herlihy, M. P., & Wing, J. M. (1990). Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems, 12(3), 463–492. https://doi.org/10.1145/78969.78972
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user