diff --git a/book/9-cpp17.md b/book/10-cpp20.md similarity index 96% rename from book/9-cpp17.md rename to book/10-cpp20.md index 6a9405f..5a48e0e 100644 --- a/book/9-cpp17.md +++ b/book/10-cpp20.md @@ -1,6 +1,6 @@ -# 第九章 扩展主题:C++17 简介 +# 第九章 展望: C++20 简介 -> 内容修订中 +> 内容修订中, 目前内容为第一版中对 C++17 的展望 ## 一、本节内容 @@ -8,14 +8,13 @@ - 扩展主题: C++17 简介 + 主要入选特性 -+ 非类型模板参数的 `auto` -+ `std::variant<>` -+ 结构化绑定(Structured bindings) + + 非类型模板参数的 `auto` + + `std::variant<>` + 变量声明的强化 + 未入选特性 -+ Concepts + + Concepts -本节对即将到来的 C++17 进行介绍,几个月前(2016 年),目前为止,还没有一个正式发布的编译器来编译 C++17 特性的代码,本节作为扩展主题,供对 C++ 的历史进程及其未来发展感兴趣的读者阅读。 +本章对即将到来的 C++17 进行介绍,几个月前(2016 年),目前为止,还没有一个正式发布的编译器来编译 C++17 特性的代码,本节作为扩展主题,供对 C++ 的历史进程及其未来发展感兴趣的读者阅读。 ## 二、主要入选特性 diff --git a/book/2-usability.md b/book/2-usability.md index c9237ff..529cd8e 100644 --- a/book/2-usability.md +++ b/book/2-usability.md @@ -559,14 +559,14 @@ extern template class std::vector; // 不在该编译文件中实例 在传统 C++ 的编译器中,`>>`一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码: ```cpp -std::vector> mtx; + std::vector> mtx; ``` 这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。甚至于下下面这种写法都能够通过编译: ```cpp template SuckType; -std::vector2)>> v; // 合法, 但不建议写出这样的代码 + std::vector2)>> v; // 合法, 但不建议写出这样的代码 ``` ### 类型别名模板 @@ -599,7 +599,7 @@ using NewType = SuckType; ```cpp template auto add(T x, U y) -> decltype(x+y) { -return x+y + return x+y } ``` @@ -610,7 +610,7 @@ return x+y ```cpp template auto add(T x, U y) -> decltype(x+y) { -return x+y + return x+y } ``` @@ -626,9 +626,9 @@ template class Magic; ```cpp class Magic, -std::map>> darkMagic; + std::vector, + std::map>> darkMagic; ``` 既然是任意形式,所以个数为0的模板参数也是可以的:`class Magic<> nothing;`。 @@ -652,7 +652,7 @@ template void printf(const std::string &str, Args... args); ```cpp template void magic(Args... args) { -std::cout << sizeof...(args) << std::endl; + std::cout << sizeof...(args) << std::endl; } ``` 我们可以传递任意个参数给 `magic` 函数: @@ -710,14 +710,14 @@ void printf(T0 t0, T... t) { // 编译这个代码需要开启 -std=c++14 template auto print(T value, Args... args) { -std::cout << value << std::endl; -return std::initializer_list{([&] { -std::cout << args << std::endl; -}(), value)...}; + std::cout << value << std::endl; + return std::initializer_list{([&] { + std::cout << args << std::endl; + }(), value)...}; } int main() { -print(1, 2.1, "123"); -return 0; + print(1, 2.1, "123"); + return 0; } ``` @@ -753,20 +753,20 @@ C++11 引入了委托构造的概念,这使得构造函数可以在同一个 ```cpp class Base { public: -int value1; -int value2; -Base() { -value1 = 1; -} -Base(int value) : Base() { // 委托 Base() 构造函数 -value2 = 2; -} + int value1; + int value2; + Base() { + value1 = 1; + } + Base(int value) : Base() { // 委托 Base() 构造函数 + value2 = 2; + } }; int main() { -Base b(2); -std::cout << b.value1 << std::endl; -std::cout << b.value2 << std::endl; + Base b(2); + std::cout << b.value1 << std::endl; + std::cout << b.value2 << std::endl; } ``` @@ -777,23 +777,23 @@ std::cout << b.value2 << std::endl; ```cpp class Base { public: -int value1; -int value2; -Base() { -value1 = 1; -} -Base(int value) : Base() { // 委托 Base() 构造函数 -value2 = 2; -} + int value1; + int value2; + Base() { + value1 = 1; + } + Base(int value) : Base() { // 委托 Base() 构造函数 + value2 = 2; + } }; class Subclass : public Base { public: -using Base::Base; // 继承构造 + using Base::Base; // 继承构造 }; int main() { -Subclass s(3); -std::cout << s.value1 << std::endl; -std::cout << s.value2 << std::endl; + Subclass s(3); + std::cout << s.value1 << std::endl; + std::cout << s.value2 << std::endl; } ``` @@ -803,10 +803,10 @@ std::cout << s.value2 << std::endl; ```cpp struct Base { -virtual void foo(); -}; -struct SubClass: Base { -void foo(); + virtual void foo(); + }; + struct SubClass: Base { + void foo(); }; ``` @@ -859,10 +859,10 @@ C++11 提供了上述需求的解决方案,允许显式的声明采用或拒 ```cpp class Magic { -public: -Magic() = default; // 显式声明使用编译器生成的构造 -Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造 -Magic(int magic_number); + public: + Magic() = default; // 显式声明使用编译器生成的构造 + Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造 + Magic(int magic_number); } ``` @@ -874,10 +874,10 @@ C++11 引入了枚举类(enumaration class),并使用 `enum class` 的语 ```cpp enum class new_enum : unsigned int { -value1, -value2, -value3 = 100, -value4 = 100 + value1, + value2, + value3 = 100, + value4 = 100 }; ``` @@ -885,8 +885,8 @@ value4 = 100 ```cpp if (new_enum::value3 == new_enum::value4) { -// 会输出 -std::cout << "new_enum::value3 == new_enum::value4" << std::endl; + // 会输出 + std::cout << "new_enum::value3 == new_enum::value4" << std::endl; } ``` @@ -899,7 +899,7 @@ std::cout << "new_enum::value3 == new_enum::value4" << std::endl; template std::ostream& operator<<(typename std::enable_if::value, std::ostream>::type& stream, const T& e) { -return stream << static_cast::type>(e); + return stream << static_cast::type>(e); } ``` diff --git a/book/3-runtime.md b/book/3-runtime.md index ca8fad9..5940f86 100644 --- a/book/3-runtime.md +++ b/book/3-runtime.md @@ -47,14 +47,14 @@ Lambda 表达式的基本语法如下: ```cpp void learn_lambda_func_1() { -int value_1 = 1; -auto copy_value_1 = [value_1] { -return value_1; + int value_1 = 1; + auto copy_value_1 = [value_1] { + return value_1; }; value_1 = 100; auto stored_value_1 = copy_value_1(); -// 这时, stored_value_1 == 1, 而 value_1 == 100. -// 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝 + // 这时, stored_value_1 == 1, 而 value_1 == 100. + // 因为 copy_value_1 在创建时就保存了一份 value_1 的拷贝 } ``` @@ -64,14 +64,14 @@ auto stored_value_1 = copy_value_1(); ```cpp void learn_lambda_func_2() { -int value_2 = 1; -auto copy_value_2 = [&value_2] { -return value_2; + int value_2 = 1; + auto copy_value_2 = [&value_2] { + return value_2; }; value_2 = 100; auto stored_value_2 = copy_value_2(); -// 这时, stored_value_2 == 100, value_1 == 100. -// 因为 copy_value_2 保存的是引用 + // 这时, stored_value_2 == 100, value_1 == 100. + // 因为 copy_value_2 保存的是引用 } ``` @@ -99,12 +99,12 @@ C++14 给与了我们方便,允许捕获的成员用任意的表达式进行 #include int main() { -auto important = std::make_unique(1); -auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int { -return x+y+v1+(*v2); -}; -std::cout << add(3,4) << std::endl; -return 0; + auto important = std::make_unique(1); + auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int { + return x+y+v1+(*v2); + }; + std::cout << add(3,4) << std::endl; + return 0; } ``` @@ -118,7 +118,7 @@ return 0; ```cpp auto add = [](auto x, auto y) { -return x+y; + return x+y; }; add(1, 2); @@ -137,17 +137,17 @@ Lambda 表达式的本质是一个函数对象,当 Lambda 表达式的捕获 #include using foo = void(int); // 定义函数指针, using 的使用见上一节中的别名语法 -void functional(foo f) { -f(1); + void functional(foo f) { + f(1); } int main() { -auto f = [](int value) { -std::cout << value << std::endl; -}; -functional(f); // 函数指针调用 -f(1); // lambda 表达式调用 -return 0; + auto f = [](int value) { + std::cout << value << std::endl; + }; + functional(f); // 函数指针调用 + f(1); // lambda 表达式调用 + return 0; } ``` @@ -160,19 +160,19 @@ C++11 `std::function` 是一种通用、多态的函数封装,它的实例可 #include int foo(int para) { -return para; + return para; } int main() { -// std::function 包装了一个返回值为 int, 参数为 int 的函数 -std::function func = foo; + // std::function 包装了一个返回值为 int, 参数为 int 的函数 + std::function func = foo; -int important = 10; -std::function func2 = [&](int value) -> int { -return 1+value+important; -}; -std::cout << func(10) << std::endl; -std::cout << func2(10) << std::endl; + int important = 10; + std::function func2 = [&](int value) -> int { + return 1+value+important; + }; + std::cout << func(10) << std::endl; + std::cout << func2(10) << std::endl; } ``` @@ -182,13 +182,13 @@ std::cout << func2(10) << std::endl; ```cpp int foo(int a, int b, int c) { -; + ; } int main() { -// 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位 -auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2); -// 这时调用 bindFoo 时,只需要提供第一个参数即可 -bindFoo(1); + // 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位 + auto bindFoo = std::bind(foo, std::placeholders::_1, 1,2); + // 这时调用 bindFoo 时,只需要提供第一个参数即可 + bindFoo(1); } ``` @@ -217,7 +217,7 @@ bindFoo(1); ```cpp std::vector foo() { std::vector temp = {1, 2, 3, 4}; -return temp; + return temp; } std::vector v = foo(); @@ -240,30 +240,30 @@ C++11 提供了 `std::move` 这个方法将左值参数无条件的转换为右 #include void reference(std::string& str) { -std::cout << "左值" << std::endl; + std::cout << "左值" << std::endl; } void reference(std::string&& str) { -std::cout << "右值" << std::endl; + std::cout << "右值" << std::endl; } int main() { -std::string lv1 = "string,"; // lv1 是一个左值 -// std::string&& r1 = s1; // 非法, 右值引用不能引用左值 -std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值 -std::cout << rv1 << std::endl; // string, + std::string lv1 = "string,"; // lv1 是一个左值 + // std::string&& r1 = s1; // 非法, 右值引用不能引用左值 + std::string&& rv1 = std::move(lv1); // 合法, std::move可以将左值转移为右值 + std::cout << rv1 << std::endl; // string, -const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期 -// lv2 += "Test"; // 非法, 引用的右值无法被修改 -std::cout << lv2 << std::endl; // string,string + const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期 + // lv2 += "Test"; // 非法, 引用的右值无法被修改 + std::cout << lv2 << std::endl; // string,string -std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期 -rv2 += "Test"; // 合法, 非常量引用能够修改临时变量 -std::cout << rv2 << std::endl; // string,string,string, + std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期 + rv2 += "Test"; // 合法, 非常量引用能够修改临时变量 + std::cout << rv2 << std::endl; // string,string,string, -reference(rv2); // 输出左值 + reference(rv2); // 输出左值 -return 0; + return 0; } ``` @@ -279,25 +279,24 @@ return 0; #include class A { public: -int *pointer; -A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; } -A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝 -A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; } -~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; } + int *pointer; + A():pointer(new int(1)) { std::cout << "构造" << pointer << std::endl; } + A(A& a):pointer(new int(*a.pointer)) { std::cout << "拷贝" << pointer << std::endl; } // 无意义的对象拷贝 + A(A&& a):pointer(a.pointer) { a.pointer = nullptr;std::cout << "移动" << pointer << std::endl; } + ~A(){ std::cout << "析构" << pointer << std::endl; delete pointer; } }; // 防止编译器优化 A return_rvalue(bool test) { -A a,b; -if(test) return a; -else return b; + A a,b; + if(test) return a; + else return b; } int main() { -A obj = return_rvalue(false); -std::cout << "obj:" << std::endl; -std::cout << obj.pointer << std::endl; -std::cout << *obj.pointer << std::endl; - -return 0; + A obj = return_rvalue(false); + std::cout << "obj:" << std::endl; + std::cout << obj.pointer << std::endl; + std::cout << *obj.pointer << std::endl; + return 0; } ``` @@ -319,19 +318,19 @@ int main() { std::string str = "Hello world."; std::vector v; -// 将使用 push_back(const T&), 即产生拷贝行为 -v.push_back(str); -// 将输出 "str: Hello world." -std::cout << "str: " << str << std::endl; + // 将使用 push_back(const T&), 即产生拷贝行为 + v.push_back(str); + // 将输出 "str: Hello world." + std::cout << "str: " << str << std::endl; -// 将使用 push_back(const T&&), 不会出现拷贝行为 -// 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销 -// 这步操作后, str 中的值会变为空 -v.push_back(std::move(str)); -// 将输出 "str: " -std::cout << "str: " << str << std::endl; + // 将使用 push_back(const T&&), 不会出现拷贝行为 + // 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销 + // 这步操作后, str 中的值会变为空 + v.push_back(std::move(str)); + // 将输出 "str: " + std::cout << "str: " << str << std::endl; -return 0; + return 0; } ``` @@ -341,25 +340,25 @@ return 0; ```cpp void reference(int& v) { -std::cout << "左值" << std::endl; + std::cout << "左值" << std::endl; } void reference(int&& v) { -std::cout << "右值" << std::endl; + std::cout << "右值" << std::endl; } template void pass(T&& v) { -std::cout << "普通传参:"; -reference(v); // 始终调用 reference(int& ) + std::cout << "普通传参:"; + reference(v); // 始终调用 reference(int& ) } int main() { -std::cout << "传递右值:" << std::endl; -pass(1); // 1是右值, 但输出左值 + std::cout << "传递右值:" << std::endl; + pass(1); // 1是右值, 但输出左值 -std::cout << "传递左值:" << std::endl; -int v = 1; -pass(v); // r 是左引用, 输出左值 + std::cout << "传递左值:" << std::endl; + int v = 1; + pass(v); // r 是左引用, 输出左值 -return 0; + return 0; } ``` @@ -382,30 +381,30 @@ return 0; #include #include void reference(int& v) { -std::cout << "左值引用" << std::endl; + std::cout << "左值引用" << std::endl; } void reference(int&& v) { -std::cout << "右值引用" << std::endl; + std::cout << "右值引用" << std::endl; } template void pass(T&& v) { -std::cout << "普通传参:"; -reference(v); -std::cout << "std::move 传参:"; -reference(std::move(v)); -std::cout << "std::forward 传参:"; -reference(std::forward(v)); + std::cout << "普通传参:"; + reference(v); + std::cout << "std::move 传参:"; + reference(std::move(v)); + std::cout << "std::forward 传参:"; + reference(std::forward(v)); } int main() { -std::cout << "传递右值:" << std::endl; -pass(1); + std::cout << "传递右值:" << std::endl; + pass(1); -std::cout << "传递左值:" << std::endl; -int v = 1; -pass(v); + std::cout << "传递左值:" << std::endl; + int v = 1; + pass(v); -return 0; + return 0; } ``` diff --git a/book/4-containers.md b/book/4-containers.md index fae515a..d6a52f5 100644 --- a/book/4-containers.md +++ b/book/4-containers.md @@ -7,6 +7,9 @@ 本节内容包括: * 对标准库的扩充: 新增容器 +* `std::byte` +* `std::any` `std::optional` `std::variant` +* `std::string_view` * `std::array` * `std::forward_list` * `std::unordered_set` @@ -42,7 +45,7 @@ std::array arr = {1,2,3,4}; // 非法, 数组大小参数必须是常 ```cpp void foo(int *p, int len) { -return; + return; } std::array arr = {1,2,3,4}; @@ -79,27 +82,27 @@ C++11 引入了两组无序容器:`std::unordered_map`/`std::unordered_multima #include int main() { -// 两组结构按同样的顺序初始化 -std::unordered_map u = { -{1, "1"}, -{3, "3"}, -{2, "2"} -}; -std::map v = { -{1, "1"}, -{3, "3"}, -{2, "2"} -}; + // 两组结构按同样的顺序初始化 + std::unordered_map u = { + {1, "1"}, + {3, "3"}, + {2, "2"} + }; + std::map v = { + {1, "1"}, + {3, "3"}, + {2, "2"} + }; -// 分别对两组结构进行遍历 -std::cout << "std::unordered_map" << std::endl; -for( const auto & n : u) -std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; + // 分别对两组结构进行遍历 + std::cout << "std::unordered_map" << std::endl; + for( const auto & n : u) + std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; -std::cout << std::endl; -std::cout << "std::map" << std::endl; -for( const auto & n : v) -std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; + std::cout << std::endl; + std::cout << "std::map" << std::endl; + for( const auto & n : v) + std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; } ``` @@ -138,33 +141,33 @@ auto get_student(int id) // 返回类型被推断为 std::tuple if (id == 0) -return std::make_tuple(3.8, 'A', "张三"); + return std::make_tuple(3.8, 'A', "张三"); if (id == 1) -return std::make_tuple(2.9, 'C', "李四"); + return std::make_tuple(2.9, 'C', "李四"); if (id == 2) -return std::make_tuple(1.7, 'D', "王五"); -return std::make_tuple(0.0, 'D', "null"); -// 如果只写 0 会出现推断错误, 编译失败 + return std::make_tuple(1.7, 'D', "王五"); + return std::make_tuple(0.0, 'D', "null"); + // 如果只写 0 会出现推断错误, 编译失败 } int main() { -auto student = get_student(0); -std::cout << "ID: 0, " -<< "GPA: " << std::get<0>(student) << ", " -<< "成绩: " << std::get<1>(student) << ", " -<< "姓名: " << std::get<2>(student) << '\n'; + auto student = get_student(0); + std::cout << "ID: 0, " + << "GPA: " << std::get<0>(student) << ", " + << "成绩: " << std::get<1>(student) << ", " + << "姓名: " << std::get<2>(student) << '\n'; -double gpa; -char grade; -std::string name; + double gpa; + char grade; + std::string name; -// 元组进行拆包 -std::tie(gpa, grade, name) = get_student(1); -std::cout << "ID: 1, " -<< "GPA: " << gpa << ", " -<< "成绩: " << grade << ", " -<< "姓名: " << name << '\n'; + // 元组进行拆包 + std::tie(gpa, grade, name) = get_student(1); + std::cout << "ID: 1, " + << "GPA: " << gpa << ", " + << "成绩: " << grade << ", " + << "姓名: " << name << '\n'; } ``` @@ -193,15 +196,15 @@ std::get(t); template boost::variant _tuple_index(size_t i, const std::tuple& tpl) { if (i == n) -return std::get(tpl); + return std::get(tpl); else if (n == sizeof...(T) - 1) -throw std::out_of_range("越界."); + throw std::out_of_range("越界."); else -return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl); + return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl); } template boost::variant tuple_index(size_t i, const std::tuple& tpl) { -return _tuple_index<0>(i, tpl); + return _tuple_index<0>(i, tpl); } ``` @@ -225,7 +228,7 @@ auto new_tuple = std::tuple_cat(get_student(1), std::move(t)); ```cpp template auto tuple_len(T &tpl) { -return std::tuple_size::value; + return std::tuple_size::value; } ``` @@ -234,8 +237,8 @@ return std::tuple_size::value; ```cpp // 迭代 for(int i = 0; i != tuple_len(new_tuple); ++i) -// 运行期索引 -std::cout << tuple_index(i, new_tuple) << std::endl; + // 运行期索引 + std::cout << tuple_index(i, new_tuple) << std::endl; ``` ## 总结 diff --git a/book/5-pointers.md b/book/5-pointers.md index 7bab07d..da200b8 100644 --- a/book/5-pointers.md +++ b/book/5-pointers.md @@ -35,17 +35,17 @@ #include void foo(std::shared_ptr i) { -(*i)++; + (*i)++; } int main() { -// auto pointer = new int(10); // 非法, 不允许直接赋值 -// 构造了一个 std::shared_ptr -auto pointer = std::make_shared(10); -foo(pointer); -std::cout << *pointer << std::endl; // 11 -// 离开作用域前,shared_ptr 会被析构,从而释放内存 -return 0; + // auto pointer = new int(10); // 非法, 不允许直接赋值 + // 构造了一个 std::shared_ptr + auto pointer = std::make_shared(10); + foo(pointer); + std::cout << *pointer << std::endl; // 11 + // 离开作用域前,shared_ptr 会被析构,从而释放内存 + return 0; } ``` @@ -87,7 +87,7 @@ std::unique_ptr pointer2 = pointer; // 非法 > ```cpp > template > std::unique_ptr make_unique( Args&& ...args ) { -> return std::unique_ptr( new T( std::forward(args)... ) ); +> return std::unique_ptr( new T( std::forward(args)... ) ); > } > ``` > @@ -100,35 +100,35 @@ std::unique_ptr pointer2 = pointer; // 非法 #include struct Foo { -Foo() { std::cout << "Foo::Foo" << std::endl; } -~Foo() { std::cout << "Foo::~Foo" << std::endl; } -void foo() { std::cout << "Foo::foo" << std::endl; } + Foo() { std::cout << "Foo::Foo" << std::endl; } + ~Foo() { std::cout << "Foo::~Foo" << std::endl; } + void foo() { std::cout << "Foo::foo" << std::endl; } }; void f(const Foo &) { -std::cout << "f(const Foo&)" << std::endl; + std::cout << "f(const Foo&)" << std::endl; } int main() { -std::unique_ptr p1(std::make_unique()); -// p1 不空, 输出 -if (p1) p1->foo(); -{ -std::unique_ptr p2(std::move(p1)); -// p2 不空, 输出 -f(*p2); -// p2 不空, 输出 -if(p2) p2->foo(); -// p1 为空, 无输出 -if(p1) p1->foo(); -p1 = std::move(p2); -// p2 为空, 无输出 -if(p2) p2->foo(); -std::cout << "p2 被销毁" << std::endl; -} -// p1 不空, 输出 -if (p1) p1->foo(); -// Foo 的实例会在离开作用域时被销毁 + std::unique_ptr p1(std::make_unique()); + // p1 不空, 输出 + if (p1) p1->foo(); + { + std::unique_ptr p2(std::move(p1)); + // p2 不空, 输出 + f(*p2); + // p2 不空, 输出 + if(p2) p2->foo(); + // p1 为空, 无输出 + if(p1) p1->foo(); + p1 = std::move(p2); + // p2 为空, 无输出 + if(p2) p2->foo(); + std::cout << "p2 被销毁" << std::endl; + } + // p1 不空, 输出 + if (p1) p1->foo(); + // Foo 的实例会在离开作用域时被销毁 } ``` @@ -141,22 +141,22 @@ struct A; struct B; struct A { -std::shared_ptr pointer; -~A() { -std::cout << "A 被销毁" << std::end; -} + std::shared_ptr pointer; + ~A() { + std::cout << "A 被销毁" << std::end; + } }; struct B { -std::shared_ptr pointer; -~B() { -std::cout << "B 被销毁" << std::end; -} + std::shared_ptr pointer; + ~B() { + std::cout << "B 被销毁" << std::end; + } }; int main() { -auto a = std::make_shared(); -auto b = std::make_shared(); -a.pointer = b; -b.pointer = a; + auto a = std::make_shared(); + auto b = std::make_shared(); + a.pointer = b; + b.pointer = a; } ``` diff --git a/book/6-regex.md b/book/6-regex.md index ece0733..538a697 100644 --- a/book/6-regex.md +++ b/book/6-regex.md @@ -87,11 +87,11 @@ C++11 提供的正则表达式库操作 `std::string` 对象,模式 `std::rege #include int main() { -std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"}; -// 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.` -std::regex txt_regex("[a-z]+\\.txt"); -for (const auto &fname: fnames) -std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl; + std::string fnames[] = {"foo.txt", "bar.txt", "test", "a0.txt", "AAA.txt"}; + // 在 C++ 中 `\` 会被作为字符串内的转义符,为使 `\.` 作为正则表达式传递进去生效,需要对 `\` 进行二次转义,从而有 `\\.` + std::regex txt_regex("[a-z]+\\.txt"); + for (const auto &fname: fnames) + std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl; } ``` @@ -101,15 +101,15 @@ std::cout << fname << ": " << std::regex_match(fname, txt_regex) << std::endl; std::regex base_regex("([a-z]+)\\.txt"); std::smatch base_match; for(const auto &fname: fnames) { -if (std::regex_match(fname, base_match, base_regex)) { -// sub_match 的第一个元素匹配整个字符串 -// sub_match 的第二个元素匹配了第一个括号表达式 -if (base_match.size() == 2) { -std::string base = base_match[1].str(); -std::cout << "sub-match[0]: " << base_match[0].str() << std::endl; -std::cout << fname << " sub-match[1]: " << base << std::endl; -} -} + if (std::regex_match(fname, base_match, base_regex)) { + // sub_match 的第一个元素匹配整个字符串 + // sub_match 的第二个元素匹配了第一个括号表达式 + if (base_match.size() == 2) { + std::string base = base_match[1].str(); + std::cout << "sub-match[0]: " << base_match[0].str() << std::endl; + std::cout << fname << " sub-match[1]: " << base << std::endl; + } + } } ``` @@ -134,7 +134,7 @@ bar.txt sub-match[1]: bar > 本节提到的内容足以让我们开发编写一个简单的 Web 框架中关于URL匹配的功能。 -> 关于这方面的开发和细节,可以通过项目课:[C++ 开发 Web 服务框架](https://www.shiyanlou.com/courses/568) 进行进一步学习。 +> ~~关于这方面的开发和细节,可以通过项目课:[C++ 开发 Web 服务框架](https://www.shiyanlou.com/courses/568) 进行进一步学习。~~ TODO: 将这部分内容补充为习题 ## 进一步阅读的参考资料 diff --git a/book/7-thread.md b/book/7-thread.md index aa03c2a..acab395 100644 --- a/book/7-thread.md +++ b/book/7-thread.md @@ -20,12 +20,12 @@ #include #include void foo() { -std::cout << "hello world" << std::endl; + std::cout << "hello world" << std::endl; } int main() { -std::thread t(foo); -t.join(); -return 0; + std::thread t(foo); + t.join(); + return 0; } ``` @@ -39,13 +39,13 @@ return 0; ```cpp void some_operation(const std::string &message) { -static std::mutex mutex; -std::lock_guard lock(mutex); + static std::mutex mutex; + std::lock_guard lock(mutex); -// ...操作 + // ...操作 -// 当离开这个作用域的时候,互斥锁会被析构,同时unlock互斥锁 -// 因此这个函数内部的可以认为是临界区 + // 当离开这个作用域的时候,互斥锁会被析构,同时unlock互斥锁 + // 因此这个函数内部的可以认为是临界区 } ``` @@ -62,14 +62,14 @@ std::mutex mtx; void block_area() { std::unique_lock lock(mtx); -//...临界区 + //...临界区 } int main() { -std::thread thd1(block_area); + std::thread thd1(block_area); -thd1.join(); + thd1.join(); -return 0; + return 0; } ``` @@ -92,15 +92,15 @@ return 0; int main() { -// 将一个返回值为7的 lambda 表达式封装到 task 中 -// std::packaged_task 的模板参数为要封装函数的类型 -std::packaged_task task([](){return 7;}); -// 获得 task 的 future -std::future result = task.get_future(); // 在一个线程中执行 task -std::thread(std::move(task)).detach(); std::cout << "Waiting..."; -result.wait(); -// 输出执行结果 -std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n'; + // 将一个返回值为7的 lambda 表达式封装到 task 中 + // std::packaged_task 的模板参数为要封装函数的类型 + std::packaged_task task([](){return 7;}); + // 获得 task 的 future + std::future result = task.get_future(); // 在一个线程中执行 task + std::thread(std::move(task)).detach(); std::cout << "Waiting..."; + result.wait(); + // 输出执行结果 + std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n'; } ``` @@ -120,46 +120,46 @@ std::cout << "Done!" << std:: endl << "Result is " << result.get() << '\n'; int main() { -// 生产者数量 -std::queue produced_nums; -// 互斥锁 -std::mutex m; -// 条件变量 -std::condition_variable cond_var; -// 结束标志 -bool done = false; -// 通知标志 -bool notified = false; + // 生产者数量 + std::queue produced_nums; + // 互斥锁 + std::mutex m; + // 条件变量 + std::condition_variable cond_var; + // 结束标志 + bool done = false; + // 通知标志 + bool notified = false; -// 生产者线程 -std::thread producer([&]() { -for (int i = 0; i < 5; ++i) { -std::this_thread::sleep_for(std::chrono::seconds(1)); -// 创建互斥锁 -std::unique_lock lock(m); -std::cout << "producing " << i << '\n'; -produced_nums.push(i); -notified = true; -// 通知一个线程 -cond_var.notify_one(); -} -done = true; -cond_var.notify_one(); + // 生产者线程 + std::thread producer([&]() { + for (int i = 0; i < 5; ++i) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + // 创建互斥锁 + std::unique_lock lock(m); + std::cout << "producing " << i << '\n'; + produced_nums.push(i); + notified = true; + // 通知一个线程 + cond_var.notify_one(); + } + done = true; + cond_var.notify_one(); }); // 消费者线程 std::thread consumer([&]() { -std::unique_lock lock(m); -while (!done) { -while (!notified) { // 循环避免虚假唤醒 -cond_var.wait(lock); -} -while (!produced_nums.empty()) { -std::cout << "consuming " << produced_nums.front() << '\n'; -produced_nums.pop(); -} -notified = false; -} + std::unique_lock lock(m); + while (!done) { + while (!notified) { // 循环避免虚假唤醒 + cond_var.wait(lock); + } + while (!produced_nums.empty()) { + std::cout << "consuming " << produced_nums.front() << '\n'; + produced_nums.pop(); + } + notified = false; + } }); producer.join(); @@ -173,7 +173,7 @@ C++11 语言层提供了并发编程的相关支持,本节简单的介绍了 ` > 本节提到的内容足以让我们使用不超过 100 行代码编写一个简单的线程池库 > -> 关于这方面的使用技巧,可以通过项目课:[100 行 C++ 代码实现线程池](https://www.shiyanlou.com/teacher/courses/565) 进行进一步巩固学习。 +> ~~关于这方面的使用技巧,可以通过项目课:[100 行 C++ 代码实现线程池](https://www.shiyanlou.com/teacher/courses/565) 进行进一步巩固学习。~~ TODO: 将这部分内容补充为习题 ## 进一步阅读的参考资料 diff --git a/book/8-filesystem.md b/book/8-filesystem.md new file mode 100644 index 0000000..a01ec2f --- /dev/null +++ b/book/8-filesystem.md @@ -0,0 +1,3 @@ +# 第八章 标准库: 文件系统 + +> TODO: 这部分内容为 C++17 新增 \ No newline at end of file diff --git a/book/8-others.md b/book/9-others.md similarity index 99% rename from book/8-others.md rename to book/9-others.md index 9f09cf5..b88001e 100644 --- a/book/8-others.md +++ b/book/9-others.md @@ -1,4 +1,4 @@ -# 第八章 其他杂项 +# 第九章 其他杂项 > 内容修订中 diff --git a/book/appendix.md b/book/appendix.md new file mode 100644 index 0000000..0f03812 --- /dev/null +++ b/book/appendix.md @@ -0,0 +1,52 @@ +# 附录:进一步阅读的学习材料 + +首先,恭喜你阅读完本书 🎉 !笔者希望本书有提起你对现代 C++ 的兴趣。 + +正如本书引言部分提到的,本书只是一本带你快速领略现代 C++ 特性的读物,而非进阶学习实践『黑魔法』的内容。笔者当然也想到了这个需求,只是这样的内容非常艰深,鲜有受众。在此,笔者列出一些能够帮助你在此书基础之上进一步学习现代 C++ 的资料。 + +- [C++ 参考](http://en.cppreference.com/w) +- [CppCon YouTube 频道](https://www.youtube.com/user/CppCon/videos) +- [每位程序员都需要知道的内存知识(英文)](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf) + + + +需要支持以下内容: + +- 语言级 bug 与修订 + + + 表达式评估顺序 + + 类型系统改进 Type Punning + + 弃用和删除的功能 + +- 语言增强 + + + [x] 结构化绑定 Structured bindings + + [ ] inline 变量 `inline` variables + + [x] 新的控制结构 `constexpr` if + + [ ] 新的聚合规则 + + [ ] `constexpr` 改进 + + [ ] 强制性 RVO 和复制 elision, Guaranteed copy elision +- 模板增强 + + [x] 折叠表达式 Fold expressions + + [ ] 类模板参数推导 Class template deduction + + [ ] `auto` non-type template parameters +- 库增强 + + [ ] `std::string_view`, `std::byte` + + [ ] `std::any`, `std::variant`, `std::optional` + + [ ] 容器改进 container 改进 + + [ ] thread 并行 STL 算法 + + [ ] 文件系统库 +- 其他特性 + + [ ] Selection statements with initializers + + [ ] Compile-time conditional statements + + [ ] Unary `statuc_assert` + + [ ] Nested namespace deinitions + + [ ] Preprocessor predicate for header testing + + [ ] Polymorphic allocators and memory resources + + [ ] Aligned `new` + + [ ] Improved insertion and splicing for associative constrainers + + [ ] Math special functions + + [ ] Variable templates for metafunctions + + [ ] Boolean logic metafunctions +... + diff --git a/book/toc.md b/book/toc.md index e120ba9..16ec87c 100644 --- a/book/toc.md +++ b/book/toc.md @@ -86,8 +86,8 @@ + `std::future` + `std::packaged_task` + `std::condition_variable` -- [**第八章 标准库: 文件系统** TODO] -- [**第九章 其他杂项**](./8-others.md) +- [**第八章 标准库: 文件系统**](./8-filesystem.md) +- [**第九章 其他杂项**](./9-others.md) + 新类型 + `long long int` + `noexcept` 的修饰和操作 @@ -95,16 +95,11 @@ + 原始字符串字面量 + 自定义字面量 + 数学库 -- **第十章 展望: ~~C++17~~ C++20 简介** - + ~~主要入选特性~~ - + ~~非类型模板参数的 `auto`~~ - + ~~`std::variant<>`~~ - + ~~变量声明的强化~~ - + 未入选特性 - + Concept - + Range - + Module - + Coroutine +- [**第十章 展望: C++20 简介**](./10-cpp20.md) + + Concept + + Range + + Module + + Coroutine 返回目录 | 上一章 | [下一章:序言](./0-preface.md)