diff --git a/1/1.1.cpp b/1/1.1.cpp new file mode 100644 index 0000000..b7c1d46 --- /dev/null +++ b/1/1.1.cpp @@ -0,0 +1,13 @@ +// +// 1.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// + +#include "foo.h" + +int main() { + add(1, 2); + return 0; +} diff --git a/1/Makefile b/1/Makefile new file mode 100644 index 0000000..96c5f97 --- /dev/null +++ b/1/Makefile @@ -0,0 +1,6 @@ +TARGET = 1.1 +all: + gcc -c foo.c + g++ 1.1.cpp foo.o -o $(TARGET) +clean: + rm -rf *.o $(TARGET) diff --git a/1/foo.c b/1/foo.c new file mode 100644 index 0000000..5c3668b --- /dev/null +++ b/1/foo.c @@ -0,0 +1,13 @@ +// +// foo.c +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// + + +#include "foo.h" + +int add(int x, int y) { + return x+y; +} diff --git a/1/foo.h b/1/foo.h new file mode 100644 index 0000000..6aa8ead --- /dev/null +++ b/1/foo.h @@ -0,0 +1,17 @@ +// +// foo.h +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// + + +#ifdef __cplusplus +extern "C" { +#endif + +int add(int x, int y); + +#ifdef __cplusplus +} +#endif diff --git a/2/2.1.cpp b/2/2.1.cpp new file mode 100644 index 0000000..ccdc6c4 --- /dev/null +++ b/2/2.1.cpp @@ -0,0 +1,32 @@ +// +// 2.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// nullptr + +#include + +void foo(char *); +void foo(int); + +int main() { + + if(NULL == (void *)0) + std::cout << "NULL == 0" << std::endl; // 该行将输出 + else + std::cout << "NULL != 0" << std::endl; + + foo(0); // 调用 foo(int) + //foo(NULL); // 该行不能通过编译 + foo(nullptr); // 调用 foo(char*) + + return 0; +} +void foo(char *ch) { + std::cout << "call foo(char*)" << std::endl; +} +void foo(int i) { + std::cout << "call foo(int)" << std::endl; +} diff --git a/2/2.2.cpp b/2/2.2.cpp new file mode 100644 index 0000000..3c115b9 --- /dev/null +++ b/2/2.2.cpp @@ -0,0 +1,34 @@ +// +// 2.2.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// constexpr + +#include +#define LEN 10 + +constexpr int len_foo() { + return 5; +} + +constexpr int fibonacci(const int n) { + return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2); +} + +int main() { + char arr_1[10]; + + char arr_2[LEN]; + + const int len = 10; + char arr_3[len]; + + char arr_5[len_foo()+5]; + + std::cout << fibonacci(10) << std::endl; + // 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 + + return 0; +} diff --git a/2/2.3.cpp b/2/2.3.cpp new file mode 100644 index 0000000..39f206c --- /dev/null +++ b/2/2.3.cpp @@ -0,0 +1,49 @@ +// +// 2.3.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// auto/decltype/尾返回类型/返回类型推导 + + +#include + +// 传统 C++ +template +R add1(T x, U y) { + return x+y; +} + +// 尾返回类型 +template +auto add2(T x, U y) -> decltype(x+y) { + return x+y; +} + +// C++14 返回类型推导 +template +auto add3(T x, U y) { + return x+y; +} + +int main() { + auto i = 5; + + int arr[10] = {0}; + auto auto_arr = arr; // 正确,对整个类型进行推导 + //auto auto_arr2[10] = arr; // 错误, 无法推导数组元素类型 + + auto x = 1; + auto y = 2; + decltype(x+y) z1; + //auto z2; // 错误, 无法推导 + + + std::cout << add1(1,1) << std::endl; + std::cout << add1(1,1) << std::endl; + std::cout << add1(1,1) << std::endl; + + + return 0; +} diff --git a/2/2.4.cpp b/2/2.4.cpp new file mode 100644 index 0000000..62b4351 --- /dev/null +++ b/2/2.4.cpp @@ -0,0 +1,30 @@ +// +// 2.4.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 区间迭代 + +#include +#include + +int main() { + int array[] = {1,2,3,4,5}; + for(auto &x : array) { + std::cout << x << std::endl; + } + + // 传统 C++ 写法 + std::vector arr(5, 100); + for(std::vector::iterator i = arr.begin(); i != arr.end(); ++i) { + std::cout << *i << std::endl; + } + + // C++11 写法 + // & 启用了引用, 如果没有则对 arr 中的元素只能读取不能修改 + for(auto &i : arr) { + std::cout << i << std::endl; + } + return 0; +} diff --git a/2/2.5.cpp b/2/2.5.cpp new file mode 100644 index 0000000..7890fba --- /dev/null +++ b/2/2.5.cpp @@ -0,0 +1,34 @@ +// +// 2.5.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 初始化列表 + + +#include + +class Foo { +private: + int value; +public: + Foo(int) {} +}; + +class Magic { +public: + Magic(std::initializer_list list) {} +}; + +void func(std::initializer_list list) { + return; +} + +int main() { + int arr[3] = {1,2,3}; // 列表初始化 + Foo foo(1); // 普通构造初始化 + + Magic magic = {1,2,3,4,5}; // 使用 initialize_list + func({1,2,3}); +} diff --git a/2/2.6.cpp b/2/2.6.cpp new file mode 100644 index 0000000..8fb3e94 --- /dev/null +++ b/2/2.6.cpp @@ -0,0 +1,76 @@ +// +// 2.6.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 模板增强 + + +#include +#include +#include + +template class std::vector; // 强行实例化 +extern template class std::vector; // 不在该编译文件中实例化模板 + + +template< typename T, typename U, int value> +class SuckType { +public: + T a; + U b; + SuckType():a(value),b(value){} +}; +// template< typename T> +// typedef SuckType, T, 1> NewType; // 不合法 + +template +using NewType = SuckType; // 合法 + +// 默认模板类型 +template +auto add(T x, U y) -> decltype(x+y) { + return x+y; +} + +// sizeof... +template +void magic(Args... args) { + std::cout << sizeof...(args) << std::endl; +} + + +// 1. 递归解参数包 +template +void printf1(T value) { + std::cout << value << std::endl; +} +template +void printf1(T value, Args... args) { + std::cout << value << std::endl; + printf1(args...); +} +// 2.初始化列表展开解参数包 +template +auto printf2(T value, Args... args) { + std::cout << value << std::endl; + return std::initializer_list{([&] { + std::cout << args << std::endl; + }(), value)...}; +} + +int main() { + + std::vector> wow; // 注意尖括号 + + NewType t; + + magic(); + magic(1); + magic(1,""); + + printf1(1, 2.3, "abc"); + printf2(1, 2.3, "abc"); + return 0; +} diff --git a/2/2.7.cpp b/2/2.7.cpp new file mode 100644 index 0000000..1d7bc30 --- /dev/null +++ b/2/2.7.cpp @@ -0,0 +1,65 @@ +// +// 2.7.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 面向对象增强 + +#include +#include +class Base { +public: + std::string str; + int value; + Base() = delete; + Base(std::string s) { + str = s; + } + + // 委托构造 + Base(std::string s, int v) : Base(s) { + value = v; + } + + // 终止重载 + virtual void foo() final { + return; + } + virtual void foo(int v) { + value = v; + } +}; +class Subclass final : public Base { +public: + double floating; + Subclass() = delete; + + // 继承构造 + Subclass(double f, int v, std::string s) : Base(s, v) { + floating = f; + } + + // 显式重载 + virtual void foo(int v) override { + std::cout << v << std::endl; + value = v; + } +}; // 合法 final + +// class Subclass2 : Subclass { +// }; // 非法, Subclass 已 final +// class Subclass3 : Base { +// void foo(); // 非法, foo 已 final +// } + +int main() { + // Subclass oops; // 非法, 默认构造已删除 + Subclass s(1.2, 3, "abc"); + + s.foo(1); + + std::cout << s.floating << std::endl; + std::cout << s.value << std::endl; + std::cout << s.str << std::endl; +} diff --git a/2/2.8.cpp b/2/2.8.cpp new file mode 100644 index 0000000..54e3196 --- /dev/null +++ b/2/2.8.cpp @@ -0,0 +1,51 @@ +// +// 2.8.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 强类型枚举 + +#include +template +std::ostream& operator<<(typename std::enable_if::value, std::ostream>::type& stream, const T& e) +{ + return stream << static_cast::type>(e); +} + +// 如果两个都定义为 value1 和 value2,将引发重定义错误 +enum Left { + left_value1 = 1, + left_value2 +}; +enum Right { + right_value1 = 1, + right_value2 +}; + +enum class new_enum : unsigned int{ + value1, + value2, + value3 = 100, + value4 = 100 +}; + +int main() { + + if (Left::left_value1 == Right::right_value2) { + std::cout << "Left::value1 == Right::value2" << std::endl; + } + + // 引发编译错误 + // if(new_enum::value1 == 1) { + // std::cout << "true!" << std::endl; + // } + if (new_enum::value3 == new_enum::value4) { + std::cout << "new_enum::value3 == new_enum::value4" << std::endl; + } + + std::cout << new_enum::value3 << std::endl; + + + return 0; +} diff --git a/3/3.1.cpp b/3/3.1.cpp new file mode 100644 index 0000000..3782544 --- /dev/null +++ b/3/3.1.cpp @@ -0,0 +1,52 @@ +// +// 3.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// lambda expression + +#include +#include + +void learn_lambda_func_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 的拷贝 +} + +void learn_lambda_func_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 保存的是引用 +} + +int main() { + learn_lambda_func_1(); + learn_lambda_func_2(); + + 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; + + // 泛型 lambda + auto generic = [](auto x, auto y) { + return x+y; + }; + + generic(1, 2); + generic(1.1, 2.2); + return 0; +} diff --git a/3/3.2.cpp b/3/3.2.cpp new file mode 100644 index 0000000..90b0277 --- /dev/null +++ b/3/3.2.cpp @@ -0,0 +1,51 @@ +// +// 3.2.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// std::function std::bind + +#include +#include + +using foo = void(int); // 定义函数指针 +void functional(foo f) { + f(1); +} + +int foo2(int para) { + return para; +} + +int foo3(int a, int b, int c) { + return 0; +} + +int main() { + + auto f = [](int value) { + std::cout << value << std::endl; + }; + functional(f); // 函数指针调用 + f(1); // lambda 表达式调用 + + // std::function 包装了一个返回值为 int, 参数为 int 的函数 + std::function func = foo2; + + 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; + + + // 将参数1,2绑定到函数 foo 上,但是使用 std::placeholders::_1 来对第一个参数进行占位 + auto bindFoo = std::bind(foo3, std::placeholders::_1, 1,2); + // 这时调用 bindFoo 时,只需要提供第一个参数即可 + bindFoo(1); + + + return 0; +} diff --git a/3/3.3.cpp b/3/3.3.cpp new file mode 100644 index 0000000..7bb5f07 --- /dev/null +++ b/3/3.3.cpp @@ -0,0 +1,38 @@ +// +// 3.3.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 右值引用 rvalue reference + + +#include +#include + +void reference(std::string& str) { + std::cout << "左值" << std::endl; +} +void reference(std::string&& str) { + 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, + + const std::string& lv2 = lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的申明周期 + // lv2 += "Test"; // 非法, 引用的右值无法被修改 + std::cout << lv2 << std::endl; // string,string + + std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象声明周期 + rv2 += "string"; // 合法, 非常量引用能够修改临时变量 + std::cout << rv2 << std::endl; // string,string,string, + + reference(rv2); // 输出左值 + + return 0; +} diff --git a/3/3.4.cpp b/3/3.4.cpp new file mode 100644 index 0000000..f0c75a0 --- /dev/null +++ b/3/3.4.cpp @@ -0,0 +1,31 @@ +// +// 3.4.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 移动语义 + +#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; } +}; +// 防止编译器优化 +A return_rvalue(bool test) { + 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; +} diff --git a/3/3.5.cpp b/3/3.5.cpp new file mode 100644 index 0000000..f8eecae --- /dev/null +++ b/3/3.5.cpp @@ -0,0 +1,32 @@ +// +// 3.5.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 移动语义 + +#include // std::cout +#include // std::move +#include // std::vector +#include // std::string + +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&&), 不会出现拷贝行为 + // 而整个字符串会被移动到 vector 中,所以有时候 std::move 会用来减少拷贝出现的开销 + // 这步操作后, str 中的值会变为空 + v.push_back(std::move(str)); + // 将输出 "str: " + std::cout << "str: " << str << std::endl; + + return 0; +} diff --git a/3/3.6.cpp b/3/3.6.cpp new file mode 100644 index 0000000..b85f2ae --- /dev/null +++ b/3/3.6.cpp @@ -0,0 +1,36 @@ +// +// 3.6.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 完美转发 + +#include +#include +void reference(int& v) { + std::cout << "左值引用" << std::endl; +} +void reference(int&& v) { + 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)); + +} +int main() { + std::cout << "传递右值:" << std::endl; + pass(1); + + std::cout << "传递左值:" << std::endl; + int v = 1; + pass(v); + + return 0; +} diff --git a/4/4.1.cpp b/4/4.1.cpp new file mode 100644 index 0000000..fd6acbc --- /dev/null +++ b/4/4.1.cpp @@ -0,0 +1,35 @@ +// +// 4.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// std::array + +#include +#include + +void foo(int *p, int len) { + for (int i = 0; i != len; ++i) { + std::cout << p[i] << std::endl; + } +} + +int main() { + std::array arr= {1,4,3,2}; + + //int len = 4; + //std::array arr = {1,2,3,4}; // 非法, 数组大小参数必须是常量表达式 + + // C 风格接口传参 + // foo(arr, arr.size()); // 非法, 无法隐式转换 + foo(&arr[0], arr.size()); + foo(arr.data(), arr.size()); + + // 更多接口使用 + std::sort(arr.begin(), arr.end()); + for(auto &i : arr) + std::cout << i << std::endl; + + return 0; +} diff --git a/4/4.2.cpp b/4/4.2.cpp new file mode 100644 index 0000000..623dc84 --- /dev/null +++ b/4/4.2.cpp @@ -0,0 +1,36 @@ +// +// 4.2.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 无序容器 + +#include +#include +#include +#include + +int main() { + // 两组结构按同样的顺序初始化 + 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::endl; + std::cout << "std::map" << std::endl; + for( const auto & n : v) + std::cout << "Key:[" << n.first << "] Value:[" << n.second << "]\n"; +} diff --git a/4/4.3.cpp b/4/4.3.cpp new file mode 100644 index 0000000..ff790b3 --- /dev/null +++ b/4/4.3.cpp @@ -0,0 +1,77 @@ +// +// 4.3.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// std::tuple 及其操作 + + +#include +#include +#include + +auto get_student(int id) +{ + if (id == 0) + return std::make_tuple(3.8, 'A', "张三"); + if (id == 1) + return std::make_tuple(2.9, 'C', "李四"); + if (id == 2) + return std::make_tuple(1.7, 'D', "王五"); + // 返回类型被推断为 std::tuple + return std::make_tuple(0.0, 'D', "null"); +} + +template +boost::variant _tuple_index(size_t i, const std::tuple& tpl) { + if (i == n) + return std::get(tpl); + else if (n == sizeof...(T) - 1) + throw std::out_of_range("越界."); + else + 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); +} + +template +auto tuple_len(T &tpl) { + return std::tuple_size::value; +} + +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'; + + 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::tuple t("123", 4.5, 6.7, 8); + std::cout << std::get(t) << std::endl; + // std::cout << std::get(t) << std::endl; // 非法, 引发编译期错误 + std::cout << std::get<3>(t) << std::endl; + + // 拼接元组 + auto new_tuple = std::tuple_cat(get_student(1), std::move(t)); + + // 迭代 + for(int i = 0; i != tuple_len(new_tuple); ++i) + // 运行期索引 + std::cout << tuple_index(i, new_tuple) << std::endl; +} diff --git a/5/5.1.cpp b/5/5.1.cpp new file mode 100644 index 0000000..9f2b0d0 --- /dev/null +++ b/5/5.1.cpp @@ -0,0 +1,66 @@ +#include +#include + +void foo(std::shared_ptr i) +{ + (*i)++; +} + + +//struct Base { +// Base() { std::cout << " Base::Base()\n"; } +// ~Base() { std::cout << " Base::~Base()\n"; } +//}; +// +//struct Subclass: public Base { +// Subclass() { std::cout << " Subclass::Subclass()\n"; } +// ~Subclass() { std::cout << " Subclass::~Subclass()\n"; } +//}; +// +//void thr(std::shared_ptr p) +//{ +// std::this_thread::sleep_for(std::chrono::seconds(1)); +// std::shared_ptr lp = p; // 即使引用计数增加也是线程安全的 +// { +// static std::mutex io_mutex; +// std::lock_guard lk(io_mutex); +// std::cout << "local pointer in a thread:\n" +// << " lp.get() = " << lp.get() +// << ", lp.use_count() = " << lp.use_count() << '\n'; +// } +//} + +int main() +{ + // auto pointer = new int(10); // 非法, 不允许直接赋值 + // 构造了一个 std::shared_ptr + auto pointer = std::make_shared(10); + auto pointer2 = pointer; // 引用计数+1 + auto pointer3 = pointer; // 引用计数+1 + + + foo(pointer); + std::cout << *pointer << std::endl; // 11 + int *p = pointer.get(); // 这样不会增加引用计数 + + std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; + std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; + std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; + + pointer2.reset(); + std::cout << "reset pointer2:" << std::endl; + std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; + std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; + std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; + + pointer3.reset(); + std::cout << "reset pointer3:" << std::endl; + std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; + std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; + std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; + // std::cout << *pointer << std::endl; // 引用计数为0时, 非法访问 + + + // 离开作用域前,pointer 会被析构,引用计数减为0, 从而释放内存 + return 0; +} diff --git a/5/5.2.cpp b/5/5.2.cpp new file mode 100644 index 0000000..40166f7 --- /dev/null +++ b/5/5.2.cpp @@ -0,0 +1,41 @@ +#include +#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; } +}; + +void f(const Foo &) { + 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 的实例会在离开作用域时被销毁 +} diff --git a/5/5.3.cpp b/5/5.3.cpp new file mode 100644 index 0000000..3b80197 --- /dev/null +++ b/5/5.3.cpp @@ -0,0 +1,28 @@ +#include +#include + +class A; +class B; + +class A { +public: + std::shared_ptr pointer; + ~A() { + std::cout << "A 被销毁" << std::endl; + } +}; +class B { +public: + std::shared_ptr pointer; + ~B() { + std::cout << "B 被销毁" << std::endl; + } +}; +int main() { + std::shared_ptr a = std::make_shared(); + std::shared_ptr b = std::make_shared(); + a->pointer = b; + b->pointer = a; + + return 0; +} diff --git a/6/6.1.cpp b/6/6.1.cpp new file mode 100644 index 0000000..d572a53 --- /dev/null +++ b/6/6.1.cpp @@ -0,0 +1,35 @@ +// +// 6.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 正则表达式库 + +#include +#include +#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::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; + } + } + } + + return 0; +} diff --git a/7/7.1.cpp b/7/7.1.cpp new file mode 100644 index 0000000..793da95 --- /dev/null +++ b/7/7.1.cpp @@ -0,0 +1,36 @@ +// +// 7.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 线程支持库 + +#include +#include +#include +void foo() { + std::cout << "hello world" << std::endl; +} + +void foo2() { + // 将一个返回值为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'; +} + +int main() { + std::thread t(foo); + + foo2(); + + t.join(); + + return 0; +} diff --git a/7/7.2.cpp b/7/7.2.cpp new file mode 100644 index 0000000..c8199a2 --- /dev/null +++ b/7/7.2.cpp @@ -0,0 +1,61 @@ +// +// 7.2.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 生产者消费者模型 + +#include +#include +#include +#include +#include +#include + +int main() +{ + // 生产者数量 + 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 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; + } + }); + + producer.join(); + consumer.join(); +} diff --git a/8/8.1.cpp b/8/8.1.cpp new file mode 100644 index 0000000..4aab0a4 --- /dev/null +++ b/8/8.1.cpp @@ -0,0 +1,50 @@ +// +// 8.1.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// noexcept + +#include + +void may_throw() { + throw true; +} +auto non_block_throw = []{ + may_throw(); +}; +void no_throw() noexcept { + return; +} + +auto block_throw = []() noexcept { + no_throw(); +}; + +int main() +{ + std::cout << std::boolalpha + << "may_throw() noexcept? " << noexcept(may_throw()) << std::endl + << "no_throw() noexcept? " << noexcept(no_throw()) << std::endl + << "lmay_throw() noexcept? " << noexcept(non_block_throw()) << std::endl + << "lno_throw() noexcept? " << noexcept(block_throw()) << std::endl; + + try { + may_throw(); + } catch (...) { + std::cout << "捕获异常, 来自 my_throw()" << std::endl; + } + + try { + non_block_throw(); + } catch (...) { + std::cout << "捕获异常, 来自 non_block_throw()" << std::endl; + } + + try { + block_throw(); + } catch (...) { + std::cout << "捕获异常, 来自 block_throw()" << std::endl; + } +} diff --git a/8/8.2.cpp b/8/8.2.cpp new file mode 100644 index 0000000..60f0594 --- /dev/null +++ b/8/8.2.cpp @@ -0,0 +1,33 @@ +// +// 8.2.cpp +// c++1x tutorial +// +// created by changkun at shiyanlou.com +// +// 字面量 + +#include +#include + +std::string operator"" _wow1(const char *wow1, size_t len) { + return std::string(wow1)+"woooooooooow, amazing"; +} + +std::string operator""_wow2 (unsigned long long i) { + return std::to_string(i)+"woooooooooow, amazing"; +} + +int main() { + std::string str = R"(C:\\What\\The\\Fxxk)"; + std::cout << str << std::endl; + + int value = 0b1001010101010; + std::cout << value << std::endl; + + + auto str = "abc"_wow1; + auto num = 1_wow2; + std::cout << str << std::endl; + std::cout << num << std::endl; + return 0; +} diff --git a/9/9.1.cpp b/9/9.1.cpp new file mode 100644 index 0000000..a791a11 --- /dev/null +++ b/9/9.1.cpp @@ -0,0 +1,7 @@ +#include +#include +int main() { + std::list l = {1, 2, 3}; + std::sort(l.begin(), l.end()); + return 0; +} diff --git a/README.md b/README.md index d6a7bdb..4065513 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,129 @@ -# cpp1x-tutorial -《C++11/14 高速上手教程》相关源码, http://cpp.ouchangkun.com +《高速上手 C++11/14》相关源码 +==== + +**本仓库包含了《[高速上手 C++11/14](http://cpp.ouchangkun.com)》中的相关代码。** + +![](./oreilly-cover.jpg) + +本书目的 +-- + +本教程虽号称高速上手教程,但实际上对 C++11/14 的相关特性做了一个较为全面的介绍,读者可以自行根据下面的目录选取感兴趣的内容进行学习,快速熟悉需要了解的内容,这从某种意义上来说,也算是高速上手了。 + +这些特性并不需要全部掌握,只需针对特定的应用场景,学习、查阅最适合自己的新特性即可。 + +值得一提的是,本教程在介绍这些特性的过程中,尽可能简单明了的介绍了这些特性产生的历史背景和技术需求,这为理解这些特性、运用这些特性提供了很大的帮助。 + +目标读者 +-- + +1. 本教程假定读者已经熟悉了传统 C++ ,至少在阅读传统 C++ 代码上不具备任何困难。换句话说,那些长期使用传统 C++进行编码的人、渴望在短时间内迅速了解 `Modern C++` 特性的人非常适合阅读本书; +2. 本教程一定程度上介绍了一些 C++1x 的**黑魔法**,但这些魔法毕竟有限,不适合希望进阶学习 C++1x 的读者,本教程的定位系**C++1x 的快速上手**。当然,希望进阶学习的读者可以使用本书来回顾并检验自己对 `Modern C++` 的熟悉度。 + +目录 +-- +- **第一章 C++11/14 简介** + + 概述 + + 被弃用的特性 + + 与 C 的兼容性 +- **第二章 语言可用性的强化** + + `nullptr` 与 `constexpr` + + 类型推导 + + `auto` + + `decltype` + + 尾返回类型、`auto` 与 `decltype` 配合 + + + + + 区间迭代 + + 基于范围的 for 循环 + + 初始化列表 + + `std::initializer_list` + + 统一初始化语法 + + 模板增强 + + 外部模板 + + 尖括号 `>` + + 类型别名模板 + + 变长参数模板 + + 面向对象增强 + + 委托构造 + + 继承构造 + + 显式虚函数重载 + + `override` + + `final` + + 显式禁用默认函数 + + 强类型枚举 +- **第三章 语言运行期的强化** + + lambda 表达式 + + lambda 表达式基础 + + 值捕获 + + 引用捕获 + + 隐式捕获 + + 表达式捕获 + + 泛型 lambda + + 函数对象包装器 + + std::function + + std::bind/std::placeholder + + 右值引用 + + 左值、右值的纯右值、将亡值、右值 + + 右值引用和左值引用 + + 移动语义 + + 完美转发 +- **第四章 对标准库的扩充: 新增容器** + + `std::array` + + `std::forward_list` + + `std::unordered_set` + + `std::unordered_map` + + `std::tuple` + + 基本操作 + + 运行期索引 + + 合并与迭代 +- **第五章 对标准库的扩充: 智能指针和引用计数** + + RAII 与引用计数 + + `std::shared_ptr` + + `std::unique_ptr` + + `std::weak_ptr` +- **第六章 对标准库的扩充: 正则表达式库** + + 正则表达式简介 + + 普通字符 + + 特殊字符 + + 限定符 + + `std::regex` 及其相关 + + `std::regex` + + `std::regex_match` + + `std::match_results` +- **第七章 对标准库的扩充: 语言级线程支持** + + `std::thread` + + `std::mutex` + + `std::unique_lock` + + `std::future` + + `std::packaged_task` + + `std::condition_variable` +- **第八章 其他杂项** + + 新类型 + + `long long int` + + `noexcept` 的修饰和操作 + + 字面量 + + 原始字符串字面量 + + 自定义字面量 +- **第九章 扩展主题: C++17 简介** + + 主要入选特性 + + 非类型模板参数的 `auto` + + `std::variant<>` + + 结构化绑定(Structured bindings) + + 变量声明的强化 + + 未入选特性 + + Concepts + +## 交流 + +1. 本教程在每节的最下方提供了评论,如果读者发现教程中内容的错误,可以使用评论或者 issue +来指出; +2. 本书依然有很多特性没有参与介绍,例如 `alignas` 内存对齐、无限制联合等,主要是考虑到这些特性的使用频次实在是太低,故没有多做介绍,若对未提及的特性有需求,笔者可以考虑将其加入`第八章`; +3. 本教程有以下交流群,有兴趣的读者可以加入,加群请注明 `github`: + +![](./qq-group.png) + +## 许可 + +本项目中代码使用 MIT 协议开源,参见[许可](./LICENSE)。 diff --git a/oreilly-cover.jpg b/oreilly-cover.jpg new file mode 100644 index 0000000..36aa7e5 Binary files /dev/null and b/oreilly-cover.jpg differ diff --git a/qq-group.png b/qq-group.png new file mode 100644 index 0000000..c567cd7 Binary files /dev/null and b/qq-group.png differ