├── README.md ├── posts ├── C++11开源项目汇总.md ├── Install GCC4.8 In Ubuntu 12.04.md ├── Lambda 函数和表达式.md ├── Right value reference and move construct.md ├── [Bjarne Stroustrup]C++本质.md ├── [译]C++阅读列表.md ├── constexpr和常量表达式.md ├── long long int 类型.md ├── 主流编译器支持情况.md ├── 初始化列表.md ├── 可变模板参数.md ├── 右尖括号.md ├── 基于范围的for循环.md ├── 增强的sizeof.md ├── 外部模板.md ├── 对象构造能力的提升.md ├── 强类型枚举.md ├── 控制和查询对象布局.md ├── 新的函数声明语法.md ├── 新的字符串常量.md ├── 无限制的 Unions.md ├── 显式使用或禁用特定的成员函数.md ├── 显式重载和 Final 操作符.md ├── 智能指针.md ├── 空指针常量.md ├── 类内部初始化.md ├── 类型别名.md ├── 类型自动推导.md ├── 线程.md ├── 统一的初始化方式.md └── 静态断言.md └── tools ├── ConvertPostsToJekyllPosts.py ├── RenameCategory.py └── _posts ├── 2013-07-13-Alternative-Function-Syntax.md ├── 2013-07-13-Control-and-query-object-alignment.md ├── 2013-07-13-Explicit-Overrides-And-Final.md ├── 2013-07-13-Explicitly-Defaulted-and-Deleted-Special-Member-Functions.md ├── 2013-07-13-Extern-Template.md ├── 2013-07-13-Initializer-lists.md ├── 2013-07-13-Install-GCC4.8-Ubuntu.md ├── 2013-07-13-Lambda-Function-And-Expressions.md ├── 2013-07-13-New-String-Literals.md ├── 2013-07-13-NewType-long-long-int.md ├── 2013-07-13-Null-Pointer-Const.md ├── 2013-07-13-Object-Construction-Improvement.md ├── 2013-07-13-R-value-Ref-And-Move-Construct.md ├── 2013-07-13-Range-based-For-Loop.md ├── 2013-07-13-Right-Angle-Bracket.md ├── 2013-07-13-Static-Assert.md ├── 2013-07-13-Strong-Type-Enum.md ├── 2013-07-13-Thread.md ├── 2013-07-13-Type-Inference.md ├── 2013-07-13-Uniform-Initialization.md ├── 2013-07-13-Unrestricted-Unions.md ├── 2013-11-04-constexpr-and-const-expression.md ├── 2013-11-04-type-alias.md ├── 2013-12-15-compiler-support.md ├── 2013-12-15-cpp-reading-list.md ├── 2013-12-29-smart-pointer.md ├── 2014-01-14-Bjarne-Stroustrup-the-essence-of-cpp.md ├── 2014-02-07-cpp11-open-source-projs.md ├── 2014-03-05-class-inner-init.md ├── 2014-03-05-va-param.md └── 2014-03-15-improved-sizeof.md /README.md: -------------------------------------------------------------------------------- 1 | # CPP1x-STUDY-RESOURCE # 2 | 3 | 项目主页: [cpp1x.org](http://cpp1x.org) 4 | 5 | 备注: `CPP1X-STUTY-RESOURCE` 中的 `1x` 是为了兼容以后的 C++ 新标准。 6 | 7 | ## 简介 ## 8 | 9 | 期盼已久的 C++ 新标准(C++0x,C++11)于 2011 年 8 月份获得一致通过。时隔近两年,主流的 C++ 编译器(GCC, VS2012, Clang)已经支持了很多 C++ 的新特性。可喜的是,就在笔者在写这份 README.md 的时候,[GCC 4.8.1](http://gcc.gnu.org/gcc-4.8/cxx0x_status.html)文档上声明已实现 C++11 标准的所有主要语言特性。 10 | 11 | 于是,我们建立了名为 `CPP1x-STUDY-RESOURCE` 的项目,用来搜集国内外各种 C++ 新标准的学习资源,供大家参考学习。,这里将是一些 “学习文档”,不是“专业”的教程。 12 | 13 | ## 目录结构 ## 14 | 15 | + Language 16 | + Compiler 17 | + Book recommend 18 | + STL: 基本上简单介绍C++11的新加入部分,不会深入。这块的了解查文档是更好的选择。 19 | 20 | ## 加入我们 ## 21 | 22 | 你可以使用 `git clone https://github.com/sib9/cpp1x-study-resource.git` 克隆到你本地,非常欢迎广大 C++ 爱好者提交请求(Pull Requests)或者通过 issue 探讨。 23 | 24 | 让我们一起建立一个正确、专业、完善的资源库。每个文档下都会有一项 `贡献者`,标明有哪些人对此文档做过贡献,我们会记住他/她的名字和他/她所做的贡献。 25 | 26 | ## 关于我们 ## 27 | 28 | [sib9](http://sib9.us) 29 | 30 | E-mail:us@sib9.us。 31 | -------------------------------------------------------------------------------- /posts/C++11开源项目汇总.md: -------------------------------------------------------------------------------- 1 | cpp11-open-source-projs|Resource|2014-02-07 2 | 3 | 以下排名不分先后: 4 | 5 | ## [facebook/folly](https://github.com/facebook/folly) ## 6 | 7 | > Folly (acronymed loosely after Facebook Open Source Library) is a library of C++11 components designed with practicality and efficiency in mind. It complements (as opposed to competing against) offerings such as Boost and of course std. In fact, we embark on defining our own component only when something we need is either not available, or does not meet the needed performance profile. -- [Overview](https://github.com/facebook/folly/blob/master/folly/docs/Overview.md) 8 | 9 | 10 | 欢迎补充~ 11 | 12 | -------------------------------------------------------------------------------- /posts/Install GCC4.8 In Ubuntu 12.04.md: -------------------------------------------------------------------------------- 1 | Install-GCC4.8-Ubuntu|Compiler|2013-07-13 2 | 3 | ## 安装 ## 4 | 5 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 6 | sudo apt-get update 7 | sudo apt-get install gcc-4.8 8 | sudo apt-get install g++-4.8 9 | 10 | ## 测试 ## 11 | 12 | 方法一: 13 | 14 | gcc-4.8 -v 15 | g++-4.8 -v 16 | 17 | 如果安装成功应输出为:gcc version 4.8.1 (Ubuntu 4.8.1-2ubuntu1~12.04) 18 | 19 | 方法二: 20 | 21 | ls /usr/bin/gcc* -l 22 | ls /usr/bin/g++* -l 23 | 24 | 我的输出结果为: 25 | 26 | -rwxr-xr-x 1 root root 306200 4月 15 08:27 /usr/bin/gcc-4.6 27 | -rwxr-xr-x 1 root root 527596 4月 22 04:47 /usr/bin/gcc-4.7 28 | -rwxr-xr-x 1 root root 716296 6月 5 22:00 /usr/bin/gcc-4.8 29 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-ar-4.7 30 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-ar-4.8 31 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-nm-4.7 32 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-nm-4.8 33 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-ranlib-4.7 34 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-ranlib-4.8 35 | 36 | Ubuntu 默认 GCC 版本是4.6,我之前曾经装过 4.7,现在装了 4.8,所以你可以看到三个版本。 37 | 38 | ## 将 4.8 设为默认 GCC 版本 ## 39 | 40 | 为了修改默认 GCC 版本,我们需要修改软链接。当然,你也可以不修改,只不过每次使用的时候都需要 gcc-4.8/g++-4.8 带上版本,不方便。 41 | 42 | sudo rm /usr/bin/gcc 43 | sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc 44 | sudo rm /usr/bin/g++ 45 | sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++ 46 | 47 | 然后, gcc -v 提示的 GCC 版本就是 4.8.1,非常 Happy。 48 | 49 | ## 测试 Demo ## 50 | 51 | 安装了可能感觉不够爽,是时候用 gcc4.8 编一把 C++11 了,才不枉废了半天劲。我找了一个简单的 demo : 52 | 53 | // Ubuntu 12.04 GCC4.8 54 | 55 | #include 56 | #include 57 | 58 | int main() 59 | { 60 | char s[] = "Hello CPP11 World!"; 61 | int Uppercase = 0; 62 | 63 | std::for_each(s, s+sizeof(s)/sizeof(char), [&Uppercase] (char c) 64 | { 65 | if (isupper(c)) 66 | Uppercase++; 67 | }); 68 | 69 | std::cout << Uppercase << " uppercase letters in: " << s << std::endl; 70 | 71 | return 0; 72 | } 73 | 74 | 注意:编译的时候加上`-std=c++11`(不加编译器也会很友善的提醒你,^_^); 75 | 76 | ## 参考资料 ## 77 | 78 | + [Ubuntu12.04安装GCC4.7并设置C++11](http://maykiller.com/2012/ubuntu-install-gcc-4-7-with-set-default/1/),非常感谢这位博主,在我多方尝试后这是一个有效且简单的做法。 79 | + 测试Demo来自:[C++11 中值得关注的极大变化(详解)](http://coolshell.cn/articles/5265.html) 80 | -------------------------------------------------------------------------------- /posts/Lambda 函数和表达式.md: -------------------------------------------------------------------------------- 1 | Lambda-Function-And-Expressions|Language|2013-07-13 2 | 3 | C++11 支持匿名函数,文档中称为 `Lambda函数`,lambda 表达式格式如下: 4 | 5 | [capture](parameters)->return-type{body} 6 | 7 | 如果 `parameters`没有值的话,括号可以省略。如果 `body` 只有一个返回状态或者返回值为 void ,`return-type` 也经常被省略掉。如下: 8 | 9 | [capture](parameters){body} 10 | 11 | 一些 lambda 函数举例: 12 | 13 | [](int x, int y) { return x + y; } // implicit return type from 'return' statement 14 | [](int& x) { ++x; } // no return statement -> lambda function's return type is 'void' 15 | []() { ++global_x; } // no parameters, just accessing a global variable 16 | []{ ++global_x; } // the same, so () can be omitted 17 | 18 | `return-type` 不指定类型的时候,C++11 使用 decltype 来解析返回值类型(比如 `decltype(x+y)`)。 19 | 20 | 返回类型可以显式的用如下的方式指定: 21 | 22 | [](int x, int y) -> int { int z = x + y; return z; } 23 | 24 | lambda 函数可以使用 lambda 函数外面的标志符。这些变量的集合通常被成为[闭包](https://en.wikipedia.org/wiki/Closure_(computer_science)#Function_objects_.28C.2B.2B.29),闭包在 lambda 表达式的 `[]` 中定义,允许是值或者引用。如下所示: 25 | 26 | [] //no variables defined. Attempting to use any external variables in the lambda is an error. 27 | [x, &y] //x is captured by value, y is captured by reference 28 | [&] //any external variable is implicitly captured by reference if used 29 | [=] //any external variable is implicitly captured by value if used 30 | [&, x] //x is explicitly captured by value. Other variables will be captured by reference 31 | [=, &z] //z is explicitly captured by reference. Other variables will be captured by value 32 | 33 | 下面的两个例子演示了 lambda 表达式的用法: 34 | 35 | std::vector some_list{ 1, 2, 3, 4, 5 }; 36 | int total = 0; 37 | std::for_each(begin(some_list), end(some_list), [&total](int x) { 38 | total += x; 39 | }); 40 | 41 | 计算 `some_list` 的加和,存储到 `total` 中(引用传递)。 42 | 43 | std::vector some_list{ 1, 2, 3, 4, 5 }; 44 | int total = 0; 45 | int value = 5; 46 | std::for_each(begin(some_list), end(some_list), [&, value, this](int x) { 47 | total += x * value * this->some_func(); 48 | }); 49 | 50 | 除 value 和 this 外,引用传递。计算得到 total 的值。只能抓取闭包中的非静态函数,lambda 和创建它的时候具有相同的存取权限,不管是否 protected/private 成员。 51 | 52 | 一旦抓取到了,不管是隐式的还是显式(的调用),闭包中类的成员范围是可测的(If this is captured, either explicitly or implicitly, then the scope of the enclosed class members is also tested.)。使用成员的时候不再需要显式的 `this->`语法。 53 | 54 | 如果闭包中引用的局部变量,它创建的更内层块引用之后调用,行为是未定义的。(If a closure object containing references to local variables is invoked after the innermost block scope of its creation, the behaviour is undefined.) 55 | 56 | lambda 函数是实现依赖于类型的函数对象(function object);类型的名字只有编译器可用。如果用户向把 lambda 函数作为参数来用的话,类型必须是一个模板类型,或者他们必须创建一个 `std::function` 或者一个相似的对象去抓取 lambda 值。 auto 关键可以用来存储 lambda 函数。 57 | 58 | auto my_lambda_func = [&](int x) { /*...*/ }; 59 | auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ }); 60 | 61 | 下面的例子存储匿名函数在变量、vectors,和 arrays 中,然后把他们的名字当参数传递使用: 62 | 63 | #include 64 | #include 65 | #include 66 | double eval(std::function f, double x = 2.0){return f(x);} 67 | int main(){ 68 | std::function f0 = [](double x){return 1;}; 69 | auto f1 = [](double x){return x;}; 70 | decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}}; 71 | std::vector fv = {f0,f1}; 72 | fv.push_back ([](double x){return x*x;}); 73 | for(int i=0;i double_values(const vector & v) 10 | { 11 | vector new_value; 12 | for (auto itr = v.begin(); itr != v.end(); ++itr) 13 | { 14 | new_value.push_back(2 * *itr); 15 | } 16 | 17 | return new_value; 18 | } 19 | 20 | `double_values` 中 `new_value` 的构造是必须的,原则上来说,有两次拷贝:一次是返回 `new_value` 时,会产生一个临时对象,另外一次是在调用 `double_value` 时产生的,比如 `v = double_values(v);`,第一次拷贝由编译器进行一定的优化,而第二次调用 `vector` 的赋值运算符,需要复制所有的数据,也就是需要新的内存,既而迭代拷贝数据,之后临时对象进行析构。 21 | 22 | ## 解决方法 ## 23 | 24 | 接上面的例子。理论上来讲,临时对象,构造->赋值->析构,构造时,申请空间;析构时,释放空间(像是句废话,其实不然)。针对这个语句来说: `v = double_values(v)`,赋值过程中,释放内存->申请内存->赋值。那么有没有一种办法,可以让临时对象和v不要做重复的事情呢?我们假设是这样:把临时对象中的内存直接为v所用,这样就省了很多事情。 25 | 26 | 在 C++11 中,提供了 move constructor 和 move assignment 来解决这种问题。move语义可以减少很多不必须要的临时对象拷贝操作,并且保证从临时对象中拿到的资源是安全的。 27 | 28 | move 语义的实现依赖于`(右值引用)rvalue-reference`。在介绍右值引用之前,先简单介绍一下左值和右值的概念: 29 | 30 | + Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue. 31 | + An lvalue is an expression whose address can be taken, a locator value--essentially, an lvalue provides a (semi)permanent piece of memory. You can make assignments to lvalues, An expression is an rvalue if it results in a temporary object 32 | 33 | ### 右值引用 ### 34 | 35 | 右值引用会和一个临时对象绑定。比如,在 C++11 之前,如果你有一个临时对象,你可以用`regular`或者`lvalue reference` 去绑定它,但是仅仅是在`const`的情况下: 36 | 37 | const string & name = get_name(); // ok 38 | string& name = get_name(); // NOT ok 39 | 40 | 这是因为临时对象生存周期所限,引用它的地址,一直它本身就消失了,这是很危险的。注意,常量引用一个临时对象,这个对象不会立即析构,但是他仍旧是一个临时对象,所以你不能修改它。 41 | 42 | 在 C++11 中,右值引用允许你为右值绑定一个可变引用,但是不能是一个左值。换句话说,右值引用可以检测到一个对象是不是临时对象。右值引用使用`&&`语法来声明而不是`&`,可以是常量,也可以是非常量。和左值引用一样,尽管你很少见到一个常量右值引用。 43 | 44 | const string && name = get_name(); // ok 45 | string && name = get_name(); // alse ok - praise be! 46 | 47 | 使用左值和右值引用重载函数,调用情况是什么样呢? 48 | 49 | void print_ref(const std::string & str) 50 | { 51 | std::cout << str << std::endl; 52 | } 53 | 54 | void print_ref(std::string && str) 55 | { 56 | std::cout << str << std::endl; 57 | } 58 | 59 | std::string name("jerryzhang"); 60 | print_ref(name); // calls the first print_ref function, taking an lvalue reference 61 | print_ref(get_name()); // calls the second print_ref function, taking a mutable rvalue reference 62 | 63 | 现在,我们有了自己的方式去判定一个引用变量指向的是临时对象还是非临时变量(permanent object)。右值引用版本的方法,像是一个俱乐部的秘密后门入口,只有你可以进入,如果你是一个临时对象的话。 64 | 65 | 了解了右值引用,是时候看看它的具体用途了。 66 | 67 | 68 | ### move constructr 和 move assignment operator ### 69 | 70 | 右值引用的最常用用途是创建move构造函数和move赋值运算符(具有相同的规则)。move构造可以避免重新申请内存,因为我们知道它已经提供了一个临时对象,我们不需要从它来复制字段,而是直接从它们上move过来。 71 | 72 | 如果字段是一个内置类型(primitive type), 比如`int`,我们就直接复制。其实我们关心的是,字段的类型是`指针`:这种情况下,不是申请内存/初始化内存,我们可以直接`偷取`指针,然后把临时对象的指针指空。我们知道临时对象已经不再用,因此可以使用它指针所指向的内存。 73 | 74 | 假如我们有下面这样一个简单的`ArrayWrapper`类: 75 | 76 | class ArrayWrapper 77 | { 78 | public: 79 | ArrayWrapper(int n) 80 | : _p_vals(new int[n]) 81 | , _size(n) 82 | {} 83 | // copy constructor 84 | ArrayWrapper(const ArrayWrapper & other) 85 | : _p_vals(new int[other._size)) 86 | , _size(other._size) 87 | { 88 | for (int i = 0; i <_size; ++i) 89 | { 90 | _p_vals[i] = other._p_vals[i]; 91 | 92 | } 93 | } 94 | ~ArrayWrapper() 95 | { 96 | delete [] _p_vals; 97 | 98 | } 99 | private: 100 | int * _p_vals; 101 | int _size; 102 | }; 103 | 104 | 注意,拷贝构造函数既申请了内存又对每个值进行了赋值,这个拷贝操作很耗费时间。下面是使用 move 构造,对效率进行了提升: 105 | 106 | class ArrayWrapper 107 | { 108 | public: 109 | // default constructor produces a moderately size array 110 | ArrayWrapper() 111 | : _p_vals(new int[64]) 112 | , _size(64) 113 | {} 114 | 115 | ArrayWrapper(int n) 116 | : _p_vals(new int[n]) 117 | , _size(n) 118 | {} 119 | 120 | // move constructor 121 | ArrayWrapper(ArrayWrapper && other) 122 | : _p_vals(other._p_vals) 123 | , size(other._size) 124 | { 125 | other._p_vals = NULL; 126 | } 127 | 128 | // copy constructor 129 | ArrayWrapper(const ArrayWrapper& other) 130 | : _p_vals(new int[other._p_vals]) 131 | , _size(other._size) 132 | { 133 | for (int i = 0; i < _size ; ++i) 134 | { 135 | _p_vals[i] = other._p_vals[i]; 136 | } 137 | } 138 | ~ArrayWrapper() 139 | { 140 | delete [] _p_vals; 141 | } 142 | private: 143 | int *_p_vals; 144 | int _size; 145 | }; 146 | 147 | `move constructor` 要比 `copy constructor` 简单且高效的多! 有两点需要注意: 148 | 149 | 1. 参数是`非常量`右值引用 150 | 2. `other._p_vals` 要置为 `NULL` 151 | 152 | 第二点是站在第一点的基础上的,如果传入的是一个`常量`的右值引用,`_p_vals`也不可能置为`NULL`。如果没有进行指针的置空的话,临时对象析构时会把指针所指向的内存进行释放,也就是我们的`move`没有真正的`move`——当我们使用已经释放了的内存,会引发崩溃。 153 | 154 | 再重复一下,只有确定参数是一个临时对象的时候才会去调用 `move constructor`——并且只有临时对象才可以被修改。反过来,如果你有一个函数返回的是临时对象,它会去调用 `copy constructor`而不是`move constructor`,因此你不能这样写代码: 155 | 156 | const ArrayWrapper getArrayWrapper(); // make the move constructor useless, the temporary is const! 157 | 158 | 仍旧有一种情况我们没有考虑到:我们需要赋值的字段是一个对象,例如,想象一下,我们有个媒体数据字段: 159 | 160 | class MetaData 161 | { 162 | public: 163 | MetaData(int size, const std::string & name) 164 | : _name(name) 165 | , _size(size) 166 | {} 167 | 168 | // copy constructor 169 | MetaData(const MetaData & other) 170 | : _name(other._name) 171 | , _size(other._size) 172 | {} 173 | 174 | // move constructor 175 | MetaData(MetaData&& other) 176 | : _name(other.name) 177 | , _size(other._size) 178 | {} 179 | 180 | std::string getName() const 181 | { 182 | return _name; 183 | } 184 | 185 | int getSize() const 186 | { 187 | return _size; 188 | } 189 | private: 190 | std::string _name; 191 | int _size; 192 | }; 193 | 194 | 现在我们的数据有一个`name`字段和`size`字段,我们不得不去修改 `ArrayWrapper`的定义: 195 | 196 | class ArrayWrapper 197 | { 198 | public: 199 | // default constructor produces a moderately sized array 200 | ArrayWrapper() 201 | : _p_vals(new int[64]) 202 | , _metadata(64, "ArrayWrapper") 203 | {} 204 | ArrayWrapper(int n) 205 | : _p_vals(new int[n]) 206 | , _metadata(n, "ArrayWrapper") 207 | {} 208 | 209 | // move constructor 210 | ArrayWrapper(ArrayWrapper && other) 211 | : _p_vals(other._p_vals) 212 | , _metadata(n, "ArrayWrapper") 213 | {} 214 | 215 | // copy constructor 216 | ArrayWrapper(const ArrayWrapper & other) 217 | : _p_vals(new int[other._metadata.getSize()]) 218 | , _metadata(other._metadata) 219 | { 220 | other._p_vals = NULL; 221 | } 222 | 223 | // copy constructor 224 | ArrayWrapper(const ArrayWrapper & other) 225 | : _p_vals(new int[other._metadata.getSize()]) 226 | , _metadata(other._metadata) 227 | { 228 | for (int i = 0; i < _metadata.getSize(); ++i) 229 | { 230 | _p_vals[i] = other._p_vals[i]; 231 | } 232 | } 233 | 234 | ~ArrayWrapper() 235 | { 236 | delete [] _p_vals; 237 | } 238 | 239 | private: 240 | int *_p_vals; 241 | MetaData _metadata; 242 | }; 243 | 244 | 这样可以工作吗?看起来就应该这样实现,不是吗?在 `ArrayWrapper` 的 `move constructor` 中调用 `MetaData` 的 `move constructor`,问题是不能这样执行。原因很简单,在`move constructor`中的`other`值,是一个右值引用?对于`MetaData`,它是一个左值,因此调用`copy constructor`而不是`move constructor`。可以这样去理解,右值是一个即将消失的对象,存活时间有限。我们向`move constructor`中传入一个临时对象,它就有个一个新的作用域,在上下文中右值表达式会被评估,临时对象干完它所做的事情,之后就不存在了。但是在我们的构造中,对象有了一个名字,它可以存活到我们整个函数的作用域,换句话说,我们在函数中使用了临时变量不止一次,这种情况下,临时对象会被定义在本地函数中。实际上,它已经上是本地函数中的一个左值,我们可以使用它的本地地址,和正常的变量一样使用。其实,我们在另外一个函数中使用到了它。如果`MetaData`中我们调用了`move constructor`而不是`copy constructor`,我们直接`move`对象,这样就危险了。 245 | 246 | // move constructor 247 | ArrayWrapper(ArrayWrapper && other) 248 | :_p_vals(other._p_vals) 249 | , _metadata(other._metadata) 250 | { 251 | // if _metadata(other._metadata) calls the move constructor, using 252 | // other._meta here would be extremely dangerous! 253 | other._p_vals = NULL; 254 | } 255 | 256 | 总结:左值和右值引用是左值表达式。不同之处在于左值引用一个左值的常量引用,而右值只是一个右值的引用。有点像指针和它所指对象的区别。指向的是右值,但是,我们用右值自身的时候,它就是一个左值。 257 | 258 | #### std::move #### 259 | 260 | 解决上面问题的办法就是使用 `std::move`,在 `` 中,`std::move` 是这样解释的:`ok, honest to God I know I have an lvalue, but I want it to be an rvalue.`,`std::move` 本身没有做任何移动的操作;它只是把一个左值转换成右值,因此,你可以在我们的 `move constructor` 中调用它来实现转换。我们代码可能会这样去实现: 261 | 262 | #include // for std::move | ps: 实际上,我没有添加这个头文件上也可以使用 std::move 263 | 264 | ArrayWrapper(ArrayWrapper&& other) 265 | :_p_vals(other._p_vals) 266 | , _metadata(std::move(other._metadata)) 267 | { 268 | other._p_vals = NULL; 269 | } 270 | 271 | 当然,我们也应该回到 `MetaData` 把它补充完整: 272 | 273 | MetaData(MetaData&& other) 274 | :_name(std::move(other._name)) // oh, blissful efficiency 275 | : _size(other._size) 276 | {} 277 | 278 | #### Move 赋值运算符 #### 279 | 280 | 我们也应该在 `move assignment operator` 中使用相同的方法来实现 `move construcotr`。 281 | 282 | #### Move构造函数和隐式生成的构造函数 #### 283 | 284 | 我们知道,在C++中,我们声明任何一种构造函数的时候,编译器就不再给你声明默认的构造函数了。这这儿也是这样的,添加一个 `move constructor` 之后,需要你自己声明和定义自己的默认构造函数。 285 | 286 | #### std::move 的工作机制 #### 287 | 288 | 你可能想过,怎么样去写一个像 `std::move` 这样的函数呢?你怎么才能实现把左值引用转换成右值引用呢?可能你已经想到了,是[类型转换](http://www.cprogramming.com/tutorial/lesson11.html)。`std::move`做了很多的调用操作,但是它的核心操作仅仅是使用 `static_cast` 转换成右值引用。也就是说,实际上你不需要使用 `move` —— 你还是要用的,尽管这里面的操作很清晰。事实是这个转换是必要的,这是一个好的习惯!意味着你的转换不会出问题,如果用 `static_cast` 替代 move 是非常危险的。你应该尽可能的使用 `std::move` 去把一个左值转换成右值,确保右值永远不会绑定到自己的左值上。 289 | 290 | ### 显式的返回一个右值引用 ### 291 | 292 | 任何时候你都应该写返回显式右值引用的函数吗?任何时候返回一个右值引用是什么意思?函数返回的对象不已经是右值了吗? 293 | 294 | 先回答第二个问题:返回一个显式的右值引用不同于返回一个对象的值。看下面的例子: 295 | 296 | int x; 297 | 298 | int getInt() 299 | { 300 | return x; 301 | } 302 | 303 | int && getRvalueInt() 304 | { 305 | // notice that it's fine to move a primitive type--remember, std::move is just a cast 306 | return std::move(x); 307 | } 308 | 309 | 第一种情况很清晰,`getInt()` 是一个右值, 是 x 的副本。通过写一些帮助函数,让我们看清这一点: 310 | 311 | void printAddress(const int & v) 312 | { 313 | cout << reinterpret_cast(&v) << endl; 314 | } 315 | 316 | v printAddress(getInt()); 317 | printAddress(x); 318 | 319 | 当你运行这段代码的时候,你会发现打印了两个不同的值。 320 | 321 | 换个方式: 322 | 323 | printAddress(getRValueInt()); 324 | printAddress(x); 325 | 326 | 打印出相同的值,因为我们返回的是一个显示的右值。 327 | 328 | 因此返回一个右值引用和不返回右值引用是不同的,这个不同在你返回一个已经存在对象,而不是临时对象的时候表现的非常明显。 329 | 330 | 现在回到是否必要去做的问题上。答案是,很多情况下不用(probably not)。大多数情况下,它有点像悬挂引用(引用存在,但是临时对象要被析构)。返回左值引用非常危险,可能对象出了作用域就不再存在了。右值引用不能保持一个对象一直存活着。` Returning an rvalue reference would primarily make sense in very rare cases where you have a member function and need to return the result of calling std::move on a field of the class from that function--and how often are you going to do that?` 331 | 332 | ### Move语义和标准库 ### 333 | 334 | 回到最原始的例子上——我们使用`vector`,我们没有控制 `vector` 类是否有 `move constructor` 或者 `move assignment operator`。幸运的是,标准委员会很明智,move 语义已经被添加到标准库中。也就意味着你现在可以高效的返回 `vectors`, `maps`, `strings`和其他标准库中的对象,好好享受 move 语义吧。 335 | 336 | ### STL容器移动 ### 337 | 338 | 实际上,标准库跟近了一步,如果在你自己实现的类中实现 move 语义,当你使用这些类对象的STL容器的时候,STL会自动使用 `std::move`,自动选择最有利的 `move-enable` 类和高效的复制操作。 339 | 340 | ## 扩展资料 ## 341 | 342 | + [C/C++中的左值](http://www.caole.net/diary/lvalue.html) 343 | + [L-Value and R-Value Expressions](http://msdn.microsoft.com/en-us/library/bkbs2cds.aspx) 344 | + [C++11 FAQ中文版:右值引用](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-an-an-rvalue-references.html) 345 | + [Move semantics and rvalue references in C++11](http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html) 346 | + [typecasting](http://www.cprogramming.com/tutorial/lesson11.html) 347 | + [static_cast](http://www.cprogramming.com/reference/typecasting/staticcast.html) 348 | 349 | 350 | ## 贡献者 ## 351 | 352 | + JerryZhang 353 | + Chengjie Qi 354 | -------------------------------------------------------------------------------- /posts/[Bjarne Stroustrup]C++本质.md: -------------------------------------------------------------------------------- 1 | Bjarne-Stroustrup-the-essence-of-cpp|Resource|2014-01-14 2 | 3 | 4 | 5 | 6 | ppt 下载地址: [http://vdisk.weibo.com/s/G-kaugh7dbcL](http://vdisk.weibo.com/s/G-kaugh7dbcL) 7 | -------------------------------------------------------------------------------- /posts/[译]C++阅读列表.md: -------------------------------------------------------------------------------- 1 | cpp-reading-list|Book Recommend|2013-12-15 2 | 3 | 原文链接: [C++ Reading List](http://www.drdobbs.com/cpp/c-reading-list/240155654?pgno=1) 4 | 5 | 6 | ## 1. [C++ Programming Language, 4th Edition](http://www.amazon.com/dp/0321563840) ## 7 | 8 | ![C++ Programming Language](http://image.cpp1x.org/CPP_Programming_Languages_01_full.jpg) 9 | 10 | 作者: [Bjarne Stroustrup](http://www.stroustrup.com/) 11 | 12 | 这本书堪称C++语言的“圣经”,表达了语言本身以及它的特性,由 C++ 之父 Stroustrup 完成。很多读者可能把 ANSI C++ 文档作为更权威的信息来源,但是它只是对于已经了解这门语言的人一个简洁的参考资源。这本书恰好相反,友好的(详尽的)解释了新特性,以及一些使用建议和应该避免的做法,让读者让容易理解一些特性。就此而言,这本书可以作为一本参考教程。 13 | 14 | 这本书没有使用等宽字体,垂直不对齐影响了美观。尽管如此,代码排版比以前的版本好多了。第二个问题是这本书没有完全的涵盖 C++ 语言,第 4 版有 1328 页,原版大约 1000 页。Stroustrup 多介绍了一些库,这些数字表明 C++ 变得更加的复杂。These concerns notwithstanding, I don't see how serious C++ programmers looking to use the new features of the language can proceed without this work. 15 | 16 | 极力推荐这本书! 17 | 18 | ## 2. [C++ Primer, 5th Edition](http://www.amazon.com/Primer-5th-Edition-Stanley-Lippman/product-reviews/0321714113) ## 19 | 20 | ![C++ Primer, 5th Edition](http://image.cpp1x.org/CPP_Primer_02_full.jpg) 21 | 22 | 作者: tanley Lippman, Josée Lajoie, Barbara Moo 23 | 24 | 多年以来,C++ Primer 都被作为 C++ 指定教程来,第五版使用 C++11 全部重写,900 页密集的页面描述了 C++ 语言的方方面面。这还是保守说法,它的内容已经远远的超过 primer 这个词可以表达的范畴。没有一节告诉你可以“快速的学习这门语言”,而且坚持不懈的用文字覆盖了语言的每一个方面和 C++ 程序员可能会遇到的问题。有没有怀疑过假如我们让析构函数作为 C++11 中提到的"deleted function"会怎么样呢? 我想过!这本书详细的回答了这个问题。 25 | 26 | 这本书只覆盖语言本身,通过讨论库和在附录中介绍了库中的头文件和算法的梗概(如果想详细的了解C++库的话,我建议看 the C++ Standard Library: A Tutorial and Reference,也就是我们下一个讨论的话题)。对比这两本书,Stroustrup 的 C++ Programming Language, 4th Ed. 是这本书的竞争对手,Stroustrup 的书主要是一个参考手册,反之,这本书更偏向应用。讨论内容形如如果你错误的使用特性会导致那些问题,包括了更多的代码,相比而言,比 Stroustrup 的书有更多的指导性。 27 | 28 | 我怀疑这本书早期是为了有语言基础的人写的。我不建议把这本书作为入门级书籍(学生或者第一次接触这门语言的人)。但是对于 C++ 开发人员,想去使用 C++11 新特性的人来说的话,这本书是绝佳选择。 29 | 30 | ## 3. [The C++ Standard Library, 2nd Edition](http://www.amazon.com/dp/0321623215) ## 31 | 32 | ![The C++ Standard Library, 2nd Edition](http://image.cpp1x.org/CPP_StandardLibrary_03_full.jpg) 33 | 34 | 作者: Nicolai Josuttis 35 | 36 | 这是 C++ 标准库经典的教程和参考手册第二版,专门为了C++11而作。不像许多书籍一样覆盖所有的库和API,它不是以解释如何进行函数调用而出名的。它把标准库(大部分STL)划分成子块,然后解释基本组件的设计。只详细讲解了一部分API,最后对其他的API进行概述。章节中会解释设计思想、内部实现、应用以及对不同的选择进行对比。 37 | 38 | 那些讨论有说服力、清晰而且非常丰富。例如,在 STL 函数对象和 lambda 用 12 页仅仅解释函数对象是什么和为什么你要用它们。之后作者又解释了预定义函数对象(用另外的12页)。这些都为后面的讨论奠定了基础: lambda 之旅。因此,它的所有讲解超过了1000页。有些解释也非常的精简,完整的代码强调关键的部分,你可以看到精确的看到一个函数怎样使用或者如何实现的。 39 | 40 | 这本书的第一版被很多C++程序员当成经典,第二版作为第一版的升级版,必定延续经典,推荐给你们。 41 | 42 | 43 | ## 4. [C++ Concurrency in Action](http://www.amazon.com/dp/1933988770) ## 44 | 45 | ![C++ Concurrency in Action](http://image.cpp1x.org/CPP_ConcurrencyInAction_04_full.jpg) 46 | 47 | 作者: Anthony Williams 48 | 49 | This book is a deep dive into concurrency using C++11 features. It's written by the primary developer and maintainer of the Boost Thread library, which is the basis for much of the language's new threading support. In sum, it's written by a highly qualified author. 50 | 51 | It starts off with the basics, assuming the reader has good (even very good) command of the language, but is new to writing parallel code. It steps through the problems posed by concurrency, the solutions and limitations of mutual exclusion, and how they're implemented in C++11. It then moves through the C++ memory model and atomic types. Finally, it digs into the design of lock-based and lock-free data structures. It's the best treatment I've seen of these topics since Herb Sutter discussed them in our pages. 52 | 53 | The book thoughtfully covers topics of real importance that are frequently overlooked in other treatments; namely, designing code for multithreading and debugging threaded applications. Of these, both treatments are too short in my view. A large part of the book is reference material (almost 130 pages on the C++ threading library alone). And another appendix is a full message-framework all laid out in code with explanations throughout. There's no doubt the author has done his homework. 54 | 55 | I have a few small complaints, but they are truly small. The first is that you must have in-depth knowledge of C++, good-enough-to-skate-by won't be enough for this book. Second, the author consigns thread pools to the final chapters under the rubric of "Advanced Threading." In my opinion, pools mark the point at which concurrency gets really interesting; but here, it's where the author stops. Predictably, given this orientation, he does not ever address the Actor model, which gets a single passing mention in the index. It's as if this approach did not exist at all, even though it's integral to several languages — just not, in Williams' view, C++. 56 | 57 | Overall, these complaints reflect my preferences, rather than any flaws that invalidate the book. Williams' work is an excellent treatment and likely to be the defining title in this area for a long time to come. 58 | 59 | If you want a closer look at content from this book, we recently published an excerpt from it, in a popular article: "Waiting for One-Off Events with Futures." 60 | 61 | ## 5.[Secure Coding in C and C++, 2nd Edition](http://www.amazon.com/Secure-Coding-2nd-Software-Engineering/dp/0321822137)## 62 | 63 | ![Secure Coding in C and C++, 2nd Edition](http://image.cpp1x.org/Secure_Code_CPP_05_full.jpg) 64 | 65 | 作者: Robert Seacord 66 | 67 | This is the definitive work on writing secure code in C/C++. This new edition, which nearly doubles the size of the original 2005 work, illustrates how much we've learned in the interim about attacks on code. In fact, in reading through this book, one feels almost overwhelmed by the variety and imagination of attacks today. However, as the author capably explains, security is more than just the implementation of counter-techniques, but weaves security throughout the implementation. Only through such a consistent mindset, he argues, can the damage wrought by future attack methods be contained. 68 | 69 | The book details numerous kinds of hacks and what can be done to prevent them or make them so difficult as to discourage the hacker. The explanations are remarkably well written and the code is clear. However, it requires a more-advanced formation than most books on programming: It needs the reader to have a fairly good idea of how C and C++ programs execute and what is happening at the machine level. Because the required information is not provided in the text, it's not possible to get the true value from the suggestions without having done this homework first. 70 | 71 | 72 | For developers who can follow along and understand the inner workings of program execution, this book is not only an excellent guide, but a revelation. For example, Seacord's discussions of how attacks are enabled by doubly freeing an allocated chunk of memory highlights a feature of this simple coding error that might be completely overlooked if you were not a security expert. The explanation is illuminating. 73 | 74 | The hands-on nature of this volume is exemplified by frequent recommendations of tools to use to verify code and lock down access mechanisms that crackers like to exploit. This is an excellent volume that will very definitely make you a more aware, and certainly better, programmer. 75 | 76 | ## 6. [Real-Time C++](http://www.amazon.com/dp/3642346871) ## 77 | 78 | ![Real-Time C++](http://image.cpp1x.org/Real_Time_CPP_06_full.jpg) 79 | 80 | 作者: Chris Kormanyos 81 | 82 | This is a gentle introduction to using C++11 in real-time projects. The author presents several basic projects and shows how he used C++11 to code them. He starts with a product that targets an Atmel AVR microcontroller, which he programs using the GCC toolchain, explaining along the way: the design, implementation in hardware and code, and finally how to flash and execute the program. It turns on LEDs under various circumstances. He then moves on to more-ambitious projects, such as writing low-level hardware drivers in C++. In the final chapters, he examines extending the standard C++ library and the STL for embedded purposes. 83 | 84 | The book is approachable and the code fairly clean. It shows that C++11 is a reasonable choice for embedded work. Overall, this is a good tutorial for C++ developers who want to get their feet wet in embedded programming, but due to the choice of sample projects, it probably won't appeal to programmers already active in that line of work. 85 | 86 | 笔者注: 87 | 88 | 只翻译了前三本书的简介,后面的没有翻译。笔者对并发、安全、Real-Time了解不多,不敢随便翻译(了解相关技术的同学,可以帮忙翻译)。说一下自己的看法: 89 | 90 | > C++ Programming Language 和 C++ Primer 这两本书选一本看就行了,对于入门,我推荐前者。有C++基础,我推荐后者。我和这篇文章作者的观点相同,C++ Primer 真的不适合作为入门书籍,门槛高,让人望而却步。 91 | > 92 | > The C++ Standard Library 这本书看过人的都知道,是非常好的一本工具书籍,目前第二版没有中文版。 93 | > 后面这三本书不适合初学者,看了简介以后,个人最想看 C++ Concurrency in Action。 94 | > 95 | > C++11 增加了很多新的特性和标准库,但是不用看太多的书籍。仅 C++ Primer, 5th Edition 和 The C++ Standard Library, 2nd Edition 这两本书足够。 96 | > 97 | > C++ 的最大问题在于它的繁杂,给程序员很多奇淫的机会,其实这本身对学 C++ 的人没有太多的好处。语言毕竟最终都是要面向应用的,执迷于技巧容易本末倒置,忽略了真正关注的东西。把 C++ 当成工具对待(从这个角度看C++11,它的确比以前强大了太多)。 98 | 99 | _这篇文章原本是答应 [博乐在线](http://blog.jobbole.com/) 要翻译的,结果因为当时没时间就没有翻译。昨天翻译才发现自己确实没有能力全部翻译,就成了现在的半成品,也就不再投稿给 博乐在线 了,抱歉。_ -------------------------------------------------------------------------------- /posts/constexpr和常量表达式.md: -------------------------------------------------------------------------------- 1 | constexpr-and-const-expression|Language|2013-11-04 2 | 3 | 常量表达式(const expression) 是指值不会改变并且在编译过程就能得到计算结果的表达式。 4 | 5 | ## constexpr 变量 ## 6 | 7 | C++11 新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式。声明为constexpr 的变量一定是一个常量,而且必须用常量表达式初始化。 8 | 9 | constexpr int mf = 20; // 20 是常量表达式 10 | constexpr int limit = mf + 1; // mf + 1 是常量表达式 11 | constexpr sz = size(); // 只有当size是一个constexpr函数时 12 | // 才是一条正确的语句 13 | 14 | ## constexpr 与 数组 ## 15 | c++11 允许声明大小为constexpr的数组。 16 | 17 | constexpr sz = size(); 18 | int data[sz] = {1,2,3}; //if sz == 3 19 | 20 | ## 指针和 constexpr ## 21 | 22 | 在 constexpr 声明中如果定义了一个指针,constexpr 仅对指针有效,与指针所指的对象无关。 23 | 24 | const int *p = nullptr; // p 是一个指向整数常量的指针 25 | constexpr int *q = nullptr; // q 是一个指向整数的常量指针 26 | 27 | p 是可变的,p指向的对象是不可变的。q是不可变的,q指向的对象是可变的。 28 | 29 | ## 参考资料 ## 30 | 31 | + C++ Primer(第5版)中文版 P59 32 | -------------------------------------------------------------------------------- /posts/long long int 类型.md: -------------------------------------------------------------------------------- 1 | NewType-long-long-int|Language|2013-07-13 2 | 3 | 在 C++03 中提供的最大整型类型是 `long int`,它保证大小至少要等于 `int` 的位数。这样导致了 `long int` 一些编译器上大小为 64-bit, 一些为 32-bit。C++11添加一个新的类型 `long long int`,它保证最小要和`long int`一样大,且不能小于 64-bit 。这个类型一开始在 C99 和标准 C 中被引入,很多 C++ 编译器之前已经作为一种扩展来支持它。 4 | 5 | ## 参考资料 ## 6 | 7 | + [Type long long int](https://en.wikipedia.org/wiki/C%2B%2B11#Type_long_long_int) 8 | 9 | 10 | -------------------------------------------------------------------------------- /posts/主流编译器支持情况.md: -------------------------------------------------------------------------------- 1 | compiler-support|Compiler|2013-12-15 2 | 3 | 主流编译对C++11的支持情况,欢迎补充: 4 | 5 | + GCC: [C++0x/C++11 Support in GCC](http://gcc.gnu.org/projects/cxx0x.html) 6 | + VS2010: [C++0x Core Language Features In VC10: The Table](http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx) 7 | + VS2012: [C++11 Features (Modern C++)](http://msdn.microsoft.com/en-us/library/hh567368\(v=vs.110\).aspx) 8 | + VS2013: [Support For C++11 Features (Modern C++)](http://msdn.microsoft.com/en-us/library/hh567368\(v=vs.120\).aspx) 9 | + Clang: [C++14, C++11 and C++98 Support in Clang](http://clang.llvm.org/cxx_status.html) -------------------------------------------------------------------------------- /posts/初始化列表.md: -------------------------------------------------------------------------------- 1 | Initializer-lists|Language|2013-07-13 2 | 3 | 译自:[https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists](https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists),非全部翻译,进行了一定调整和扩充。 4 | 5 | 6 | C++03 的初始化列表特性是从 C 语言继承过来的,结构体或者数组可以用大括号包含的参数列表来初始化(按照定义的顺序依次初始化)。这种初始化方式递归的,因此结构体数组或者结构体包含其他结构体也可以这么用。 7 | 8 | struct object 9 | { 10 | float first; 11 | int second; 12 | }; 13 | object scalar = {0.43f, 10}; 14 | object an_array[3] = { {13.4f, 3}, {43.28f, 29}, {5.934f, 17} }; 15 | 16 | C++03 的初始化列表特性,只能用于结构体或者符合POD的类。C++11 对这个特性进行了扩展,可以用户有所的类包括标准库容器,比如 `std::vector`。 17 | 18 | C++11 使用模板`std::initializer_list`来实现使用列表初始化这一技术,构造函数或者其他函数都可以把它作为参数。比如: 19 | 20 | template 21 | class my_list 22 | { 23 | public: 24 | my_list() {} 25 | 26 | my_list(const std::initializer_list & x) { 27 | for (auto ite=x.begin(); ite!=x.end(); ++ite) { 28 | items_.push_back(*ite); 29 | } 30 | } 31 | void show_list() { 32 | copy(items_.begin(), items_.end(), std::ostream_iterator(std::cout, " ")); 33 | std::cout << std::endl; 34 | } 35 | private: 36 | std::vector items_; 37 | }; 38 | 39 | 对于一个函数: 40 | 41 | void print_init_list(const std::initializer_list x) { 42 | copy(x.begin(), x.end(), std::ostream_iterator(std::cout, " ")); 43 | std::cout << std::endl; 44 | } 45 | 46 | 值得注意的是,需要使用`{}`来初始化一个 `std::initializer_list` 对象,比如上面的函数调用方式为 `print_init_list({5, 4, 3, 2, 1})`。列表只能被构造和复制一次,初始化列表是常量,不要妄图去修改它的值。 47 | 48 | C++11 标准容器也可以用以下的方式去初始化: 49 | 50 | std::list test_list = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 51 | std::vector test_string = {"Make", "One", "Program", "Do", "One", "Thing", "Well"}; 52 | 53 | 54 | ## 扩展资料 ## 55 | 56 | + [Initializer lists](https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists) 57 | + [C++11: Initializer lists](http://oopscenities.net/2011/05/09/c0x-initializer-lists/) 58 | -------------------------------------------------------------------------------- /posts/可变模板参数.md: -------------------------------------------------------------------------------- 1 | va-param|Language|2014-03-05 2 | 3 | ## 可变模板参数 ## 4 | 在c/c++语言中有很多可变参数的函数,类似于printf,sscanf等。而模板编程中则一直没有可变模板参数的支持,在c++11中加入了这一特性,并因而产生了新的标准库容器tuple。详细见如下代码: 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | /* 13 | //c/c++ type 14 | void printf(const char *s) 15 | { 16 | while (*s) 17 | { 18 | if (*s == '%' && *(++s) != '%') 19 | throw std::runtime_error("invalid format string: missing arguments"); 20 | std::cout << *s++; 21 | } 22 | }*/ 23 | 24 | //c++11 type 25 | //not implements well 26 | template 27 | void cxx11_printf(const char* s, T value, Args... args) 28 | { 29 | while (*s) 30 | { 31 | if (*s == '%' && *(++s) != '%') 32 | { 33 | std::cout << value; 34 | cxx11_printf(*s ? ++s : s, args...); 35 | return; 36 | } 37 | std::cout << *s++; 38 | } 39 | assert(false); 40 | } 41 | 42 | int main(int argc,char* argv[]) 43 | { 44 | cxx11_printf("%d %s",1,"hello world"); 45 | return 0; 46 | } 47 | 48 | 上面的代码向你展示了怎样使用可变模板。 49 | 50 | ## tuple ## 51 | 52 | 下面展示了C++11标准库中应用可变参数模板产生的新容器tuple的用法: 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | using namespace std; 60 | 61 | struct foo_data 62 | { 63 | friend ostream& operator<< (ostream& out,const foo_data& data); 64 | public: 65 | foo_data(const char _val) : val(_val){} 66 | public: 67 | char val = 'a'; 68 | }; 69 | ostream& operator<< (ostream& out,const foo_data& data) 70 | { 71 | out< tupInfo = make_tuple('x', string("C++ 11")); 79 | cout<<"tuple : "<(tupInfo)<<"\t"<(tupInfo)<> infoList = { 83 | make_tuple(0,foo_data('a'),string("this is a")), 84 | make_tuple(1,foo_data('b'),string("this is b")), 85 | make_tuple(2,foo_data('c'),string("this is c")), 86 | make_tuple(3,foo_data('d'),string("this is d")) 87 | }; 88 | cout<<"tuple list one : "<(tupleval)<<"\t"; 92 | cout<(tupleval)<<"\t"; 93 | cout<(tupleval)<<"\t"; 94 | cout<(0,foo_data('a'),string("this is a")), 101 | tuple(1,foo_data('b'),string("this is b")), 102 | tuple(2,foo_data('c'),string("this is c")), 103 | tuple(3,foo_data('d'),string("this is d")) 104 | }; 105 | cout<<"tuple list two : "<(tupleval)<<"\t"; 109 | cout<(tupleval)<<"\t"; 110 | cout<(tupleval)<<"\t"; 111 | cout<>"(注,在我的第一本C++书中,">>"被称为析取运算符,而"<<"被称为插入运算符,我感觉那样理解挺不错的)。 然而,在模板的嵌套声明中,程序员需要在两个尖括号之间加上一个空格,否则会引发一个编译错误。 6 | 7 | C++11 提供了新的解析规则,多个右尖括号的时候,会优先为模板解析。但是有括号的时候,会先解析括号: 8 | 9 | template class SomeType; 10 | std::vector2>> x1; // Interpreted as a std::vector of SomeType 2>, 11 | // which is not legal syntax. 1 is true. 12 | std::vector2)>> x1; // Interpreted as a std::vector of SomeType, 13 | // which is legal C++11 syntax, (1>2) is false. 14 | 15 | ## 扩展资料 ## 16 | 17 | + [Right angle bracket](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 18 | -------------------------------------------------------------------------------- /posts/基于范围的for循环.md: -------------------------------------------------------------------------------- 1 | Range-based-For-Loop|Language|2013-07-13 2 | 3 | 译自 [Range-based for loop](https://en.wikipedia.org/wiki/C%2B%2B11#Range-based_for_loop) 4 | 5 | 6 | C++11 扩展一种新的遍历列表的 for 循环语法(range-base for,基于范围的 for 循环),使得遍历列表非常简单了。可以遍历 C 式的数组、初始化列表(initializer lists),和定义了 begin()/end() 函数(返回迭代器)的类型。所有的标准库容器都可以使用 range-base for 来遍历: 7 | 8 | std::vector v; 9 | v.push_back(1); 10 | v.push_back(2); 11 | v.push_back(3); 12 | v.push_back(4); 13 | v.push_back(5); 14 | v.push_back(6); 15 | 16 | for (int i : v) { 17 | std::cout << i << " " ; 18 | } 19 | std::cout << std::endl; 20 | 21 | for (auto ite : v) { 22 | std::cout << ite << " "; 23 | } 24 | std::cout << std::endl; 25 | 26 | 27 | for (int& i : v) { 28 | ++i; 29 | } 30 | 31 | 32 | for (auto i : v) { 33 | std::cout << i << " "; 34 | } 35 | std::cout << std::endl; 36 | 37 | 38 | ## 扩展资料 ## 39 | + [C++11 range-based for loops](http://www.cprogramming.com/c++11/c++11-ranged-for-loop.html) 推荐阅读 40 | + [Range-based for loop](https://en.wikipedia.org/wiki/C%2B%2B11#Range-based_for_loop) 41 | 42 | -------------------------------------------------------------------------------- /posts/增强的sizeof.md: -------------------------------------------------------------------------------- 1 | improved-sizeof|Language|2014-03-15 2 | 3 | ## 增强的sizeof ## 4 | 5 | 在c++98/03中无法对类内部成员变量使用sizeof运算符,及时能用,也是通过比较hack的方式进行。而在C++11中,你可以对类内部的成员变量进行sizeof操作,见如下代码: 6 | 7 | #include 8 | #include 9 | using namespace std; 10 | 11 | struct foo_data 12 | { 13 | public: 14 | foo_data(const char _val) : val(_val){} 15 | public: 16 | char val = 'a'; 17 | }; 18 | 19 | int main(int argc,char* argv[]) 20 | { 21 | cout<<"sizeof foo_data::val is "<; 8 | 9 | C++11 引入外部模板这一概念,将强制编译器在特定位置开始实例化,使用 extern 来显式组织编译器在某个编译单元内实例化。使用如下代码: 10 | 11 | extern template class std::vector; 12 | 13 | 这样告诉编译器不要在该编译单元内将该模板实例化(因为已经在别的单元实例化了)。 14 | 15 | 16 | 以上来自[外部模板](http://zh.wikipedia.org/wiki/C%2B%2B11#.E5.A4.96.E9.83.A8.E6.A8.A1.E6.9D.BF),我对语言稍作组织。另,这个概念比较好理解,就不再举例说明了。 17 | 18 | -------------------------------------------------------------------------------- /posts/对象构造能力的提升.md: -------------------------------------------------------------------------------- 1 | Object-Construction-Improvement|Language|2013-07-13 2 | 3 | 译自: [Object construction improvement](https://en.wikipedia.org/wiki/C%2B%2B11#Object_construction_improvement) 4 | 5 | 在 C++03 中,构造一个类不允许用调用这个类的其他构造函数;这样会导致每个构造函数都要构造所有的类成员,或者调用一个公共的成员函数,如下: 6 | 7 | class SomeType { 8 | int number; 9 | public: 10 | SomeType(int new_number) : number(new_number) {} 11 | SomeType() : number(42) {} 12 | }; 13 | 14 | class SomeType { 15 | int number; 16 | private: 17 | void Construct(int new_number) { 18 | number = new_number; 19 | } 20 | public: 21 | SomeType(int new_number) { 22 | Construct(new_number); 23 | } 24 | SomeType() { 25 | Construct(42); 26 | } 27 | }; 28 | 29 | 基类的构造函数不能直接暴露给派生类;每个派生类必须实现构造函数,即便是基类构造函数已经很完善了。类的非常量数据成员不能在声明的地方初始化,且只能在构造函数中初始化。 30 | 31 | C++11 提供了所有这些问题的解决方案。 32 | 33 | C++11 允许构造函数调用其他的同等构造函数(peer constructors)(也就是"委托")。添加少量的代码就可以实现构造函数去调用其他构造函数。Java, C#, D 等语言都有委托。 34 | 35 | 语法如下: 36 | 37 | class SomeType { 38 | int number; 39 | public: 40 | SomeType(int new_number) : number(new_number) {} 41 | SomeType() : SomeType(42) {} 42 | }; 43 | 44 | 注意,这种情况下,`new_number`成为了默认参数。新的语法允许默认值(42)表现到实现上,而不是一个接口——受益的是库代码的维护者,直接把默认值'烤'到调用站,而构造委托允许修改值但不需要重新编译代码库。 45 | 46 | 需要注意的是:C++03 认为对象构造是在构造函数执行完毕进行构造的,但是 C++11 认为对象构造是在任何一个构造函数执行完毕。多个构造函数都允许执行,意味着每个委托构造函数可以构造一个自己拥有类型的对象。派生类构造函数在它们基类的所有委托构造函数执行完毕(基类全部构造结束)再执行的。 47 | 48 | 对于基类构造函数,C++11允许类去指定基类的那个构造函数去继承。这意味着:C++11 编译器会生成代码去执行继承操作,把派生类转发给基类。注意,这是一个有或者无的(all-or-nothing)特性;或者所有的基类的构造函数转发或者一个都没有转发。对于多重继承的限制,例如,类的构造函数不能继承有多个签名的两个类。并且不能构造在派生类中已经存在的基类签名。 49 | 50 | 语法如下 51 | 52 | class BaseClass { 53 | pubic: 54 | BaseClass(int value); 55 | }; 56 | 57 | class DerivedClass : public BaseClass { 58 | public: 59 | using BaseClass::BaseClass; 60 | }; 61 | 62 | 对于成员初始化,C++11 允许下面的语法: 63 | 64 | class SomeClass { 65 | public: 66 | SomeClass() {} 67 | explicit SomeClass(int new_value) : value(new_value) {} 68 | private: 69 | int value = 5; 70 | }; 71 | 72 | 如果构造函数没有重写它的话,所有类的构造函数都会把值初始化为 5。上面空的构造函数会初始化为5,但是有参的那个构造函数根据参数进行赋值。 73 | 74 | 它也能用构造函数或者统一的初始化方法,取代上面的赋值操作。 75 | 76 | 77 | ## 扩展资料 ## 78 | 79 | + [Object construction improvement](https://en.wikipedia.org/wiki/C%2B%2B11#Object_construction_improvement) 80 | -------------------------------------------------------------------------------- /posts/强类型枚举.md: -------------------------------------------------------------------------------- 1 | Strong-Type-Enum|Language|2013-07-13 2 | 3 | 译自: [Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint](http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html); 4 | 5 | C++03 的枚举本质上就是整型,它们可以和整型,和其它的枚举类型做比较。实际上,你并不希望不同枚举之间进行比较,就好像你不希望把钉子的种类和牙刷的种类做比较一样。 6 | 7 | 旧式枚举的另外一个限制是枚举的值是没有作用域的,换句话说,你不能两个枚举共享同一个名字,比如: 8 | 9 | enum Color {RED, GREEN, BLUE}; 10 | enum Feeling {EXCITED, MOODY, BLUE}; // error: redeclaration of ‘BLUE’ 11 | 12 | C++11 提供了强类型枚举——枚举类(enum classes),如下方式声明: 13 | 14 | `class`意味着每一个枚举类型实际上都是不一样的,不能和其它类型做比较。强类型枚举,枚举类有很好的作用域。每个枚举值作用域被限制在枚举类中。这意味着,想要存取枚举值,你必须要这么用: 15 | 16 | Color color = Color::GREEN; 17 | if (Color::RED == color) { 18 | // the color is red; 19 | } 20 | 21 | 如果你想用旧式的枚举,仍旧是可用的。很大程度上,是可以和既有的代码相兼容的。教你一招,现在你可以直接在值前面加上枚举名:Color::RED.但是这种情况下,不能解决命名冲突的问题,它只是更清晰了一些。 22 | 23 | 枚举类比旧式枚举类型另外一个优势是,你可以使用前向声明一个强枚举类型,意味着你可以这样写代码: 24 | 25 | enum class Mood; 26 | void assessMode (Mood m); 27 | enum class {EXCITED, MOODY, BLUE}; 28 | 29 | Why would this be useful? Forward declarations are often about the physical layout of code on disk into different files or to provide opaque objects as part of an API. In the first case, where you care about the physical disk layout, using a forward declaration allows you to declare an enum type in the header file while putting specific values into the cpp file. This lets you change the list of possible enum values quite frequently without forcing all dependent files to recompile. In the second case, an enum class can be exposed as a type-safe but otherwise opaque value returned from one API function to be passed into another API function. The code using the API need not know the possible values the type can take on. Since the compiler still knows about the type, it can enforce that variables declared to work with that type are not confused with variables working with another type. (译者注:这段话不太理解,所有没有翻译,欢迎补充翻译。) 30 | 31 | 枚举类的最后一个好处是你可以你的枚举的大小,你可以使用任何签名的或者未签名的整型类。默认是 int ,但是你也可以使用 char, unsigned long 等等。这样可以确保跨平台编译器的兼容性。 32 | 33 | enum class Colors : char { RED = 1, GREEN = 2, BLUE = 3 }; 34 | 35 | 但是在在 C++11 中,在指定类型大小上,我们可以用更好的方式,使用 `cstdint`。 36 | 37 | C++ 在类型方面存在一个问题,比如说,你想要一个 32位 的整型,但是 int 可能在不同体系架构下有不同的大小。在 C++11 中,C99 的头文件 `stdint.h` 包含在 `cstdint` 中。`cstdint` 头文件包含了诸如 `std::int8_t`, `std::int32_t` 和 `int64_t`(对应的 unsigned 版本是从 `std::uint8_t` 开始的)的类型。 38 | 39 | 下面是使用新类型和枚举类完美配合,告诉跨平台编译器和体系架构你的枚举大小: 40 | 41 | enum class Colors: std::int8_t { RED = 1, GREEN = 2, BLUE = 3}; 42 | 43 | ## 扩展资料 ## 44 | 45 | + [Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint](http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html) 46 | + [Strongly typed enumerations](https://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations) 47 | -------------------------------------------------------------------------------- /posts/控制和查询对象布局.md: -------------------------------------------------------------------------------- 1 | Control-and-query-object-alignment|Language|2013-07-13 2 | 3 | + 内容为个人理解,不保证正确。 4 | 5 | 6 | C++11 可以显示的控制和查询内存布局方式。 7 | 8 | 通过使用 `alignof` 可以得到对象的内存布局字节基数,比如: 9 | 10 | struct char_align { 11 | char c1; 12 | char c2; 13 | }; 14 | 15 | `alignof(char_align)` 得到的值为 `1`. 16 | 17 | struct short_int_align { 18 | short int si1; 19 | short int si2; 20 | }; 21 | 22 | `alignof(short_int_align)` 得到的值 `2` . 23 | 24 | struct test_align { 25 | int i; 26 | char c; 27 | float f; 28 | double d; 29 | long int li; 30 | }; 31 | 32 | `alignof(short_int_align)` 得到的值是 `4`. 33 | 34 | 如果之前了解过 C/C++ 字节对齐的朋友应该一点就明白了,我这里不解释了,不明白的话,建议去看一下字节对齐这个技术。 35 | 36 | `alignas` 与之相反,可以显式的指定字节对其方式,指定的参数必须是 2 的倍数。eg: 37 | 38 | struct alignas(8) test_alignas { 39 | int i; 40 | char c; 41 | float f; 42 | double d; 43 | long int li; 44 | }; 45 | 46 | 这么做指定了 test_alignas 按照 8 字节对齐。再使用 `alignof(test_alignas)` 得到的值就是 8 . 47 | 48 | ## 扩展资料 ## 49 | 50 | + [What does static_assert do, and what would you use it for?](http://stackoverflow.com/questions/1647895/what-does-static-assert-do-and-what-would-you-use-it-for) 51 | + [Control and query object alignment](https://en.wikipedia.org/wiki/C%2B%2B11#Control_and_query_object_alignment) 52 | -------------------------------------------------------------------------------- /posts/新的函数声明语法.md: -------------------------------------------------------------------------------- 1 | Alternative-Function-Syntax|Language|2013-07-13 2 | 3 | 译自 [Alternative function syntax](https://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax) 4 | 5 | 6 | 标准 C 函数声明语法对于 C 语言的特性来说是非常完美的。C++ 是从 C 语言扩展来而的,它保持了 C 的基本语言并且进行了一些必需的扩展。然后,随着 C++ 的语言复杂化,它有了很多的局限。特别是在模板函数的声明。下面的例子在 C++03 中是不允许的: 7 | 8 | template 9 | Ret adding_func(const Lhs & lhs, const Rhs & rhs) { return lhs + rhs; } // Ret must be the type of lhs+rhs 10 | 11 | 因为 Ret 的类型是由 lhs 和 rhs 共同决定的(比如 lhs 是 char , rhs 是 int,返回的应该是个 int),尽管 C++11 有了 `decltype` 可以进行类型推导,但是下面的使用也是不可以的: 12 | 13 | template 14 | decltype(lsh+rhs) adding_func(const Lhs & lhs, const Rhs & rhs) { return lhs + rhs; } // Not legal C++11 15 | 16 | 不合法的原因是 lhs 和 rhs 在函数定义之前出现了,直到编译器解析到函数原型的后半部分, lhs 和 rhs 才是有意义的。 17 | 18 | 为了解决这个问题,C++11 引入了一种新的函数声明语法,即`尾部返回值(trailing-return-type)`: 19 | 20 | template 21 | auto adding_func(const Lhs & lhs, const Rhs & rhs) -> decltype(lhs+rhs) { return lhs + rhs; } 22 | 23 | 这种语法可以应用到很多普通函数的声明和定义: 24 | 25 | struct SomeStruct { 26 | auto func_name(int x, int y) -> int; 27 | }; 28 | 29 | auto SomeStruct::func_name(int x, int y) -> int { 30 | return x + y; 31 | } 32 | 33 | 此种用法实际与lambda函数是很统一的,并不是很莫名其妙的语法。 34 | 35 | 36 | 37 | ## 扩展资料 ## 38 | 39 | + [Alternative function syntax](https://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax) 40 | + [Improved Type Inference in C++11: auto, decltype, and the new function declaration syntax](http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html) 41 | -------------------------------------------------------------------------------- /posts/新的字符串常量.md: -------------------------------------------------------------------------------- 1 | New-String-Literals|Language|2013-07-13 2 | 3 | 译自: [New string literals](https://en.wikipedia.org/wiki/C%2B%2B11#New_string_literals) 4 | 5 | C++03 提供了两种类型的字符串字面值。第一种,用两个双引号包含, 生成一个`const char`类型空终结符的(null-terminated)数组; 第二种,用 `L""` 定义的,生成`const wchar_t`的空终结符数组, `wchar_t`是一个为定义大小且语义上表示宽字符。字面量类型既不支持 UTF-8, UTF-16 也不支持 Unicode 编码。 6 | 7 | 字符类型的定义明确的表达成它至少需要存储 UTF-8 的 8-bit 编码,足够包含任何编译器基本运算的字符集。之前在 C++ 自身标准在中仅仅为字母使用,C 标准保证至少 8 位。 8 | 9 | C++11支持三种Unicode编码,即:UTF-8,UTF-16,UTF-32。除了之前的 `char` 类型外,C++11 添加了两种新的字符类型:`char_16_t`和`char32_t`,专门用来存储`UTF-16`和`UTF-32`。 10 | 11 | 下面展示了怎么为每一种编码创建字符串字面量: 12 | 13 | u8"I'am a UTF-8 string." 14 | u"This's a UTF-16 string." 15 | U"This's a UTF-32 string." 16 | 17 | 第一种类型是 `const char[]`, 第二种类型是 `char16_t[]`,第三种类型是`char32_t[]`。 18 | 19 | 当构造 Unicode 字符串字面量的时候,它经常直接向字符串中插入 Unicode 代码点。为了实现这一点,C++ 允许下面的代码语法: 20 | 21 | u8"This is Unicode Character: \u2018." 22 | u"This is a bigger Unicode Character: \u2018." 23 | U"This is Unicode Character: \U00002018." 24 | 25 | `\u`后面是十六进制数字,不需要 `0x` 前缀。`\u` 代表 `16-bit` 代码点;可以用 `\U` 来表示 `32-bit` 代码点。必须要输入有效的 Unicode 代码点。例如,在 `U+D800~U+DFFF`中间的数字不能使用,它们是为`UTF-16`代理对预留的。 26 | 27 | 它也在避免使用转义字符串上是非常有用的,尤其是使用 XML 文件字面量,脚本语言,或者正则表达式,C++ 提供了一个原生的字符串字面量: 28 | 29 | R"(The String Data \ Stuff ")" 30 | R"delimiter(The String Data \ Stuff " )delimiter" 31 | 32 | 在第一种情况下,`"(` 和 `)"` 之间的部分是字符串。`"` 和 `\` 字符不再需要转义。在第二种情况下,以`delimiter(`开始,则只能以`)delimiter`结束。字符串 `delimiter` 可以把任何字符串在长度上改造成 16 个字符,允许用户在原生字符串字面量上使用 ")" 字符。例如,`R"delimiter((a-z))delimiter` 等价于 `(a-z)`. 33 | 34 | 原生字符串可以使用任何的宽字符常量做前缀: 35 | 36 | u8R"XXX(I'am a "raw UTF-8" string.)XXX" 37 | uR"*(This is a "raw UTF-16" string.)*" 38 | UR"(This is a "raw UTF-32" string.)" 39 | 40 | 41 | ## 扩展资料 ## 42 | 43 | + [New string literals](https://en.wikipedia.org/wiki/C%2B%2B11#New_string_literals) 44 | -------------------------------------------------------------------------------- /posts/无限制的 Unions.md: -------------------------------------------------------------------------------- 1 | Unrestricted-Unions|Language|2013-07-13 2 | 3 | 译自:[Unrestricted unions](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 4 | 5 | 在 C++03 中 union 的成员不能是一个对象类型。例如, unions 不能包含任何定义了一个 non-trivial 的构造函数。C++11 解除了一些限制: 6 | 7 | + Unions 可以包含有 non-trivial 对象的成员; 8 | + 如果这样的话,编译器不再提供隐式的构造函数,需要自己去定义. 9 | 10 | 下面是一个简单的 C++11 unions 定义: 11 | 12 | #include // Required for placement 'new' 13 | 14 | struct Point { 15 | Point() {} 16 | Point(int x, int y) : x_(x), y_(y) {} 17 | int x_, y_; 18 | }; 19 | 20 | union U { 21 | int z; 22 | double w; 23 | Point p; // Illegal in C++03; legal in C++11 24 | U() { 25 | new (&p) Point(); // Due to the Point member, a constructor definition is now required. 26 | } 27 | }; 28 | 29 | 30 | ## 扩展资料 ## 31 | 32 | + [Unrestricted unions](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 33 | + [What is a non-trivial constructor in C++?](http://stackoverflow.com/questions/3899223/what-is-a-non-trivial-constructor-in-c) 34 | -------------------------------------------------------------------------------- /posts/显式使用或禁用特定的成员函数.md: -------------------------------------------------------------------------------- 1 | Explicitly-Defaulted-and-Deleted-Special-Member-Functions|Language|2013-07-13 2 | 3 | 译自:[Explicitly defaulted and deleted special member functions](https://en.wikipedia.org/wiki/C%2B%2B11#Explicitly_defaulted_and_deleted_special_member_functions) 4 | 5 | 在 C++03 中,编译器为类提供了默认的构造函数,拷贝构造函数,赋值运算符(operator=),析构函数。程序员通过自己定义这些函数来重载他们,C++ 也定义了几个全局运算符(operator=,operator new)可供类来使用,程序员也可以重载它们。 6 | 7 | 然而,这种可控性太弱了。让一个类天生就不可复制,我们可以声明一个 `private` 的拷贝构造函数和赋值运算符,但是不实现它们。使用它们的时候,会产生一个链接错误。 8 | 9 | 在这种情况下,如果类中定义了任何一种构造函数,编译器不再生成默认的构造函数。很多情况下,这样做是很有用的。但是,如果一个类既在指定了构造函数的同时,编译器又可以生成一个默认的构造函数就更好了。 10 | C++ 允许显式的使用或者删除指定的成员函数。例如,下面声明了使用中的默认构造函数: 11 | 12 | struct SomeType { 13 | SomeType() = default; 14 | SomeType(OtherType value); 15 | }; 16 | 17 | 下面的类是不可复制的: 18 | 19 | struct NonCopyable { 20 | NonCopyable() = default; 21 | NonCopyable(const NonCopyable&) = delete; 22 | NonCopyable & operator=(const NonCopyable&) = delete; 23 | }; 24 | 25 | `= delete` 表示这个函数的不可调用的,也可以用这种方法来禁止函数传入特定的参数: 26 | 27 | struct NoInt { 28 | void f(double i); 29 | void f(int) = delete; 30 | }; 31 | 32 | 编译器不允许 `NoInt::f` 传入一个 `int` 类型的参数,而不是悄悄的把 `int` 转成 `double`。下面这种方法可以禁掉除 `double` 之外的所有参数: 33 | 34 | struct OnlyDouble { 35 | void f(double d); 36 | template void f(T) = delete; 37 | }; 38 | 39 | ## 扩展资料 ## 40 | 41 | + [Explicitly defaulted and deleted special member functions](https://en.wikipedia.org/wiki/C%2B%2B11#Explicitly_defaulted_and_deleted_special_member_functions) 42 | -------------------------------------------------------------------------------- /posts/显式重载和 Final 操作符.md: -------------------------------------------------------------------------------- 1 | Explicit-Overrides-And-Final|Language|2013-07-13 2 | 3 | 译自: [Explicit overrides and final](https://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final) 4 | 5 | 在 C++03 中,重载基类的虚函数,不能达到重载的效果。比如: 6 | 7 | struct Base { 8 | virtual void some_func(float a) 9 | { 10 | std::cout << "Base: " << a << std::endl; 11 | } 12 | }; 13 | 14 | struct Derived : Base { 15 | virtual void some_func(int a) 16 | { 17 | std::cout << "Derived: " << a << std::endl; 18 | } 19 | }; 20 | 21 | 当用基类指针指向派生类对象时,调用`some_func`并不调用派生类的 `some_func`(也就是上面说的不能达到重载的效果,但是编译器并没有报错)。这是因为它们有不同的函数签名(signature),它们是两个不同的虚函数。常常会因为修改了基类的函数参数,却没有同步派生类的函数参数,引发很多问题(多态)。 22 | 23 | C++11 提供显式重载语法来解决这个问题: 24 | 25 | struct NewBase { 26 | virtual void some_func(float a) 27 | { 28 | std::cout << "NewBase: " << a << std::endl; 29 | } 30 | }; 31 | 32 | struct NewDerived : NewBase { 33 | virtual void some_func(int a) override // ill-formed - doesn't override a base class method 34 | { 35 | std::cout << "NewDerived: " << a << std::endl; 36 | } 37 | }; 38 | 39 | 显示的使用`override`告诉编译器检查基类(们)去检查是否有虚函数有确定的签名,如果没有的话,编译器会报错。(译者住:这样的话,相当与把问题放大了,让用户明确自己的使用)。 40 | 41 | C++11 也提供了阻止派生类重载基类函数的方法,是用`final`来实现的: 42 | 43 | struct Base1 final{}; 44 | 45 | struct Derived : Base final {}; // ill-formed because the class Base1 has been marked final 46 | 47 | struct Base2 { 48 | virtual void f() final; 49 | }; 50 | 51 | struct Derived2 : Base2 { 52 | virtual void f(); // ill-formed because the class Base1 has been marked final 53 | }; 54 | 55 | `Base1`是`final`类,`virtual void f() final`是`final`函数,显示告诉编译器该类/虚函数是不能用来派生/重载的, 56 | 57 | 注意,不管是`override`还是`final`都不是语言的关键字,它们是技术上的标志符(`technically identifiers`);只有在指定的上下文中才有特定的含义。在其他情况下,它们都是无意义的标志符。 58 | 59 | ## 扩展资料 ## 60 | 61 | + [Explicit overrides and final](https://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final) 62 | -------------------------------------------------------------------------------- /posts/智能指针.md: -------------------------------------------------------------------------------- 1 | smart-pointer|STL|2013-12-29 2 | 3 | C++98只支持一个智能指针[auto_ptr](http://www.cplusplus.com/reference/memory/auto_ptr/?kw=auto_ptr),然而auto_ptr实在是太简陋了(赋值导致的指针转移,一直为人诟病)。 4 | 5 | C++11废弃了`auto_ptr`,引入了两种智能指针: 6 | 7 | 1. `shared_ptr` 8 | 9 | 共享拥有对象的所有权,使用引用技术来实现。具体可看: [http://www.cplusplus.com/reference/memory/shared_ptr/](http://www.cplusplus.com/reference/memory/shared_ptr/)。 10 | 11 | 2. `unique_ptr` 12 | 13 | 唯一拥有对象的所有权,auto\_ptr的替代品,但是修复了auto\_ptr的缺陷,指针对象以赋值的方式转让。详细可看: [http://www.cplusplus.com/reference/memory/unique_ptr/](http://www.cplusplus.com/reference/memory/unique_ptr)。 14 | 15 | C++11允许为智能指针设置一个删除器,当设定一个删除器时,智能指针析构会用指定的删除器取代默认的delete操作。如果你使用指针指针管理的资源不是用new分配的,记得一定要为这个智能指针设定一个删除器。 16 | 17 | 其实C++11还存在一种"弱"智能指针,叫[weak\_ptr](http://www.cplusplus.com/reference/memory/weak_ptr)。它不能控制所指对象的生存期,指向由 shared\_ptr 管理的对象(不会改变引用计数),主要用途是解决智能指针循环引用导致的内存泄漏。 18 | 19 | 我们知道不可以为auto_ptr传入一个动态数组,否则会导致未定义的行为(new[]->delete)。在C++11中的这两个智能指针都可以传入一个动态数组,不过略有不同。 20 | 21 | + 对于`share_ptr`,传入一个动态数组,你需要同时为它指定一个删除器。如: `shared_ptr sp(new int[1024], [](int *p) {delete[] p;});` 可以看出,我们写一个lambda表达式,用 delete[] 去释放一个数组。 22 | + 对于`unique_ptr`就没这么麻烦了,你可以直接使用`[]`用来标识你要传入一个动态数组。如: `unique_ptr sp(new int[1024]);`。 23 | 24 | 25 | ## 扩展资料 ## 26 | 27 | + [C++ smart pointers wikipedia](http://en.wikipedia.org/wiki/Smart_pointer#C.2B.2B_smart_pointers) 28 | + [When is std::weak_ptr useful?](http://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful) -------------------------------------------------------------------------------- /posts/空指针常量.md: -------------------------------------------------------------------------------- 1 | Null-Pointer-Const|Language|2013-07-13 2 | 3 | 早在 1972 年,常量`0`就扮演着整型常量和空指针常量的双重身份。为了解决`0`的二义性,C 使用预处理宏`NULL`表示空指针,一般被定义为`((void*)0)`或者`0`。 C++ 与 C 不同,只允许 0 作为空指针常量。这样会使函数重载不好交互(interacts poorly): 4 | 5 | void foo(char *); 6 | void foo(int); 7 | 8 | 如果`NULL`定义为`0`,`foo(NULL);`将会调用`foo(int)`,这通常不是程序员所期待的行为,也违反了代码的直观性。 9 | 10 | C++11 通过引入一个新的关键字来纠正这个问题,用来标志空指针常量: `nullptr`。它的类型是`nullptr_t`(用来隐式转换到任何指针或者成员指针(pointer-to-member)类型,不能转换成整型类型,除了 bool)。 11 | 12 | 为了向后兼容,`0`依旧是一个空的指针常量。 13 | 14 | char *pc = nullptr; // ok 15 | char *pi = nullptr; // ok 16 | bool b = nullptr; // ok, b is false 17 | int i = nullptr; // error 18 | 19 | foo(nullptr); // call foo(char *), not foo(int); 20 | 21 | -------------------------------------------------------------------------------- /posts/类内部初始化.md: -------------------------------------------------------------------------------- 1 | class-inner-init|Language|2014-03-05 2 | 3 | ## 类内部初始化 ## 4 | c++11中能像c#/java一样在成员变量声明的同时初始化,可通过赋值初始化,也可通过列表初始化,见如下代码: 5 | 6 | #include 7 | using namespace std; 8 | 9 | struct foo_data 10 | { 11 | public: 12 | foo_data() : val('b'){} 13 | 14 | char val = 'a'; 15 | char val2{'x'}; 16 | }; 17 | 18 | int main(int argc,char* argv[]) 19 | { 20 | foo_data data; 21 | cout< There is absolutely no difference between both. 14 | > 15 | > If you take a look at the standard : 16 | > 17 | > 7.1.3 The typedef specifier [dcl.typedef ] 18 | > 19 | > A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id. 20 | > 21 | > 7.3.3 The using declaration [namespace.udecl] 22 | > 23 | > If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2), the name introduced by the using-declaration is treated as a typedef-name. 24 | > 25 | > However from this page : http://en.cppreference.com/w/cpp/language/type_alias 26 | > 27 | > It is said : 28 | > 29 | > Type aliases are similar to typedefs, however, have the advantage of working with templates. 30 | > 31 | > It is seems that this : 32 | > 33 | > // template type alias 34 | > template using ptr = T*; 35 | > // the name 'ptr' is now an alias for pointer to T 36 | > ptr x; 37 | > Is only possible with the using directive. 38 | > 39 | > And do not forget that this is a C++11 feature. Some compilers do not support it yet. 40 | 41 | 我想,上面的这段回复足够回答你的问题了。 42 | -------------------------------------------------------------------------------- /posts/类型自动推导.md: -------------------------------------------------------------------------------- 1 | Type-Inference|Language|2013-07-13 2 | 3 | 4 | 在 C++03 中(包括C),使用一个变量比如显式的指定它的类型。然而,随着模板类型和模板元编程技术的的引入,对象的类型,详细的定义一个函数的返回值,可能不易表达。因此,临时变量的存储变的很难,可能需要去了解具体模板元库的实现细节。 5 | 6 | C++11用两种方式来解决这种问题。第一种是使用 auto 关键字来定义直接初始化的变量类型(也就是所谓的类型推导,根据初始化的值来推导类型)。 7 | 8 | auto some_strange_callable_type = std::bind(&some_function, _2, _1, some_object); 9 | auto other_variable = 5; 10 | 11 | `some_strange_callable_type` 类型比较简单,不用管 `std::bind` 的实现细节(参数、返回值,是否重载),它的类型对于编译器来说很容易得到(编译器语义分析的一部分),而用户判定它的类型不是那么容易。 12 | 13 | `other_variable` 是一个 `int`, 和整型字面量类似。 14 | 15 | 此外,`decltype`可以在编译的时候判断出一个表达式的类型(第二种方式),例如: 16 | 17 | int some_int; 18 | decltype(some_int) other_integer_variable = 5; 19 | 20 | 和 `auto` 配合使用非常牛逼,auto 变量的具体类型只有编译器知道,而 decltype 对于大量的运算符重载和类型特化的表示非常用用。 21 | 22 | 使用 auto 可以减少很多代码冗余,比如说,以前我们是这样写的: 23 | 24 | for (std::vector::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 25 | 26 | 现在可以这样写: 27 | 28 | for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 29 | 30 | 代码更短了(而且假如类型很长的话,我们不用再费劲的去记忆类型的名字,我觉得这个用法太叼了)。尤其是在模板类型嵌套的时候,比如: 31 | 32 | std::map >::const_iterator 33 | 34 | 当然,这种情况下使用 `typedef` 也是一种好方法。 35 | 36 | `decltype`和`auto`可能推导出来的类型不同: 37 | 38 | #include 39 | int main() { 40 | const std::vector v(1); 41 | auto a = v[0]; // a has type int 42 | decltype(v[1]) b = 1; // b has type const int&, the return type of 43 | // std::vector::operator[](size_type) const 44 | auto c = 0; // c has type int 45 | auto d = c; // d has type int 46 | decltype(c) e; // e has type int, the type of the entity named by c 47 | decltype((c)) f = c; // f has type int&, because (c) is an lvalue 48 | decltype(0) g; // g has type int, because 0 is an rvalue 49 | } 50 | 51 | 个人感觉,站在这个角度来说,decltype 比 auto 功能更强大一些; 反过来,auto 比 decltype 更精巧。 52 | 53 | `decltype((variable))`的结果永远是引用,而`decltype(variable)`结果只有当 variable 本身就是一个引用时才是引用。 54 | 55 | ## auto 与 函数 ## 56 | auto与普通函数,模板函数,lambda函数结合使用,你会瞬间感觉c++变得优雅了很多。 57 | 58 | #include 59 | #include 60 | using namespace std; 61 | 62 | template 63 | void myprint(T& var) 64 | { 65 | cout<; 75 | 76 | auto printfunc = normalfunc; 77 | for_each(data,data+datalen,printfunc); 78 | 79 | return 0; 80 | } 81 | 82 | ## 参考资料 ## 83 | 84 | + [Type inference](https://en.wikipedia.org/wiki/C%2B%2B11#Type_inference) 85 | -------------------------------------------------------------------------------- /posts/线程.md: -------------------------------------------------------------------------------- 1 | Thread|Language|2013-07-13 2 | 3 | 译自:[C++11 threads, locks and condition variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables)。看到如此好文,原谅我的情不自禁,向原作者致谢! 4 | 5 | 6 | ---- 7 | 8 | ## 线程(Threads) ## 9 | 10 | [std::thread](http://en.cppreference.com/w/cpp/thread/thread) 类代表一个可执行的线程,在 `` 下。`std::thread` 可以和普通函数,lambdas 函数,仿函数(实现了 `opertor()` 的类)一起工作。此外,它允许你为你的线程函数传入任意数量的参数。 11 | 12 | #include 13 | 14 | void func() 15 | { 16 | // do some work 17 | } 18 | 19 | int main() 20 | { 21 | std::thread t(func); 22 | t.join(); 23 | 24 | return 0; 25 | } 26 | 27 | `t` 是一个执行 `func` 的线程对象,`join` 阻塞调用线程(这里是住线程),直到该线程运行结束。线程函数的返回值将被忽略的,但是,线程函数可以传入任意数量的参数。 28 | 29 | void func(int i, double d, const std::string& s) 30 | { 31 | std::cout << i << ", " << d << ", " << s << std::endl; 32 | } 33 | 34 | 35 | int main() 36 | { 37 | std::thread t(func, 1, 12.50, "sample"); 38 | t.join(); 39 | 40 | return 0; 41 | } 42 | 43 | 44 | 虽然可以给线程函数传递任意多的参数,但是都是以值传递的方式传参的。如果需要引用传参,传递的参数必须使用 [std::ref 或者std::cref](http://en.cppreference.com/w/cpp/utility/functional/ref) 进行转换。eg: 45 | 46 | void func(int& a) 47 | { 48 | a++; 49 | } 50 | 51 | int main() 52 | { 53 | int a = 42; 54 | std::thread t(func, std::ref(a)); 55 | t.join(); 56 | 57 | std::cout << a << std::endl; 58 | 59 | return 0; 60 | } 61 | 62 | 参数输出为 43, 如果没有 `std::ref` 的转换,输出的值应为 42。 63 | 64 | 除 `join` 之外,线程类也提供了其它的方法: 65 | 66 | + [swap](http://en.cppreference.com/w/cpp/thread/thread/swap) : 把两个线程的相关操作(underlying handles)互换。 67 | + [detach](http://en.cppreference.com/w/cpp/thread/thread/detach) : 允许线程对象继续独立的运行。Detach 的线程不再可连接(你不用等它们了)。 68 | 69 | int main() 70 | { 71 | std::thread t(funct); 72 | t.detach(); 73 | 74 | return 0; 75 | } 76 | 77 | 值得注意的是,如果线程函数抛出一个异常,用普通的 `try-catch` 块是捕捉不到异常的。换句话说,下面这样是不行的: 78 | 79 | try 80 | { 81 | std::thread t1(func); 82 | std::thread t2(func); 83 | 84 | t1.join(); 85 | t2.join(); 86 | } 87 | catch(const std::exception& ex) 88 | { 89 | std::cout << ex.what() << std::endl; 90 | } 91 | 92 | 放大异常可以在线程内部捕捉,然后把它存到之后可以访问到的地方。 93 | 94 | std::mutex g_mutex; 95 | std::vector g_exceptions; 96 | 97 | void throw_function() 98 | { 99 | throw std::exception("Something wrong happened"); 100 | } 101 | 102 | void func() 103 | { 104 | try 105 | { 106 | throw_function(); 107 | } 108 | catch(...) 109 | { 110 | std::lock_guard lock(g_mutex); 111 | g_exceptions.push_back(std::current_exception()); 112 | } 113 | } 114 | 115 | int main() 116 | { 117 | g_exceptions.clear(); 118 | 119 | std::thread t(func); 120 | t.join(); 121 | 122 | for(auto & e : g_exceptions) 123 | { 124 | try 125 | { 126 | if (e != nullptr) 127 | { 128 | std::rethrow_exception(e); 129 | } 130 | } 131 | catch(const std::exception & e) 132 | { 133 | std::cout << e.what() << std::endl; 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | 关于捕捉和放大异常更多资料可以阅读这里:[Handling C++ exceptions thrown from worker thread in the main thread](http://binglongx.wordpress.com/2010/01/03/handling-c-exceptions-thrown-from-worker-thread-in-the-main-thread/) 和 [How can I propagate exceptions between threads?](http://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads) 141 | 142 | 另外,`` 头文件在 `std::this_thread` 提供了很多有用的函数: 143 | 144 | + [get_id](http://en.cppreference.com/w/cpp/thread/get_id): 返回当前线程的 ID; 145 | + [yield](http://en.cppreference.com/w/cpp/thread/yield): 告诉调度程序,运行其他线程(在你处于忙等待的时候非常有用); 146 | + [sleep_for](http://en.cppfreference.com/w/cpp/thread/sleep_for): 阻塞当前线程直到指定的时段(sleep_duration); 147 | + [sleep_util](http://en.cppreference.com/w/cpp/thread/sleep_until): 阻塞当前线程直到指定的时间(sleep_time); 148 | 149 | ## 锁(Locks) ## 150 | 151 | 在最后一个例子中,访问 `g_exceptions` 向量我需要进行同步来确保在同一时间只有一个线程在进行 push 操作。因此,我使用了 mutex 。C++11 在 `` 头文件中提供了四种 mutex 来做同步操作。 152 | 153 | + [mutex](http://en.cppreference.com/w/cpp/thread/mutex): 提供了核心函数 [lock()](http://en.cppreference.com/w/cpp/thread/mutex/lock), [unlock()](http://en.cppreference.com/w/cpp/thread/mutex/unlock) 和 非阻塞的 [try_lock()](http://en.cppreference.com/w/cpp/thread/mutex/try_lock) 函数(判断 mutex 是否可用); 154 | + [recursive_mutex](http://en.cppreference.com/w/cpp/thread/recursive_mutex): 允许相同线程多次获得 mutex; 155 | + [timed_mutex](http://en.cppreference.com/w/cpp/thread/timed_mutex): 和 mutex 类相似,但是它有自己的两个核心方法 [try_lock_for()](http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for) 和 [ry_lock_until() ](http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until) 用来尝试在指定的时间段或者时间点获取 mutex ; 156 | + [recursive_timed_mutex](http://en.cppreference.com/w/cpp/thread/recursive_timed_mutex): timed\_mutex 和 recursive\_mutex 的综合体。 157 | 158 | 下面是使用 `std::mutex` 的例子(注意 `get_id()` 和 `sleep_id()` 的用法): 159 | 160 | #include 161 | #include 162 | #include 163 | #include 164 | 165 | std::mutex g_lock; 166 | 167 | void func() 168 | { 169 | g_lock.lock(); 170 | 171 | std::cout << "entered thread " << std::this_thread::get_id() << std::endl; 172 | std::this_thread::sleep_for(std::chrono::seconds(rand() % 10)); 173 | std::cout << "leaving thread " << std::this_thread::get_id() << std::endl; 174 | 175 | g_lock.unlock(); 176 | } 177 | 178 | int main() 179 | { 180 | srand((unsigned int)time(0)); 181 | 182 | std::thread t1(func); 183 | std::thread t2(func); 184 | std::thread t3(func); 185 | 186 | t1.join(); 187 | t2.join(); 188 | t3.join(); 189 | 190 | return 0; 191 | } 192 | 193 | 输出可能是这样: 194 | 195 | entered thread 10144 196 | leaving thread 10144 197 | entered thread 4188 198 | leaving thread 4188 199 | entered thread 3424 200 | leaving thread 3424 201 | 202 | 203 | `lock()` 和 `unlock` 方法简单明了,第一次锁住 mutex, 如果 mutex 不可用的话进行阻塞操作,之后对 mutex 进行解锁。 204 | 205 | 下面的例子展示了单个的线程安全容器(内部实际用的是 `std::vector`)。这个容器有类似于 `add()` 的操作,添加单个元素和 `addrange` 添加多个多个元素(其实是多次调用 `add()` )。 206 | 207 | 标注:下面的例子其实并不是真正线程安全的,有几个原因包括 va_args 的使用。并且,`dump()` 方法不应该属于 container。例子的目的仅仅在于讲解关于 mutex 的概念,而不是一个完整的,无错的,线程安全的容器。 208 | 209 | template 210 | class container 211 | { 212 | std::mutex _lock; 213 | std::vector _elements; 214 | public: 215 | void add(T element) 216 | { 217 | _lock.lock(); 218 | _elements.push_back(element); 219 | _lock.unlock(); 220 | } 221 | 222 | void addrange(int num, ...) 223 | { 224 | va_list arguments; 225 | 226 | va_start(arguments, num); 227 | 228 | for (int i = 0; i < num; i++) 229 | { 230 | _lock.lock(); 231 | add(va_arg(arguments, T)); 232 | _lock.unlock(); 233 | } 234 | 235 | va_end(arguments); 236 | } 237 | 238 | void dump() 239 | { 240 | _lock.lock(); 241 | for(auto e : _elements) 242 | std::cout << e << std::endl; 243 | _lock.unlock(); 244 | } 245 | }; 246 | 247 | void func(container& cont) 248 | { 249 | cont.addrange(3, rand(), rand(), rand()); 250 | } 251 | 252 | int main() 253 | { 254 | srand((unsigned int)time(0)); 255 | 256 | container cont; 257 | 258 | std::thread t1(func, std::ref(cont)); 259 | std::thread t2(func, std::ref(cont)); 260 | std::thread t3(func, std::ref(cont)); 261 | 262 | t1.join(); 263 | t2.join(); 264 | t3.join(); 265 | 266 | cont.dump(); 267 | 268 | return 0; 269 | } 270 | 271 | 当你运行上面程序的时候,会发现会进入死锁。原因是容器在释放 mutex 之前请求了多次。这时候你就需要使用 `std::recusive_mutex` 了,它允许线程请求同一个 mutex 多次。可以请求的最大次数没有指定,但是假如到达了请求上限,调用 lock 会抛出一个 `std::system_error` 的异常。修改上面的代码比较简单,只需要使用 `std::recursive_mutex` 代替 `std::mutex`。 272 | 273 | template 274 | class container 275 | { 276 | std::recursive_mutex _lock; 277 | // ... 278 | }; 279 | 280 | 输出类似于: 281 | 282 | 6334 283 | 18467 284 | 41 285 | 6334 286 | 18467 287 | 41 288 | 6334 289 | 18467 290 | 41 291 | 292 | 机智的你可能注意到了每一次 `func` 每一次调用都生成了相同的数字序列。这是因为种子是局部线程的,调用 `srand()` 只能从主线程上初始化种子。其他的工作线程没有被初始化,所以你每次得到的种子都是一样的。 293 | 294 | 显式的锁或者解锁可能会导致一些问题,比如忘了解锁或者和请求顺序不同的解锁可能会导致死锁。标准提供了几个类和函数帮助你解决这个问题。包装类(wrapper classes)允许使用 RAII 风格(在代码块中自动加锁和解锁)把 mutexs 一致化。这些包装器有: 295 | 296 | + [lock_guard](http://en.cppreference.com/w/cpp/thread/lock_guard) : 当对象构造它的时候尝试去请求自己的 mutex (调用 lock),当对象析构它的时候会自动释放 mutex (调用 unlock()),这是一个不能拷贝的类; 297 | + [unique_lock](http://en.cppreference.com/w/cpp/thread/unique_lock) : 和 lock\_guard不同,它是一个通用的 mutex 包装器。提供了延迟锁(defferd locking), 时间锁(time locking), 递归锁(recursive locking), 转移所有权而用条件变量。这个类也是不可拷贝的类,但是他支持 move 操作。 298 | 299 | 使用这些包装器重写上面的容器类之后是这样的: 300 | 301 | template 302 | class container 303 | { 304 | std::recursive_mutex _lock; 305 | std::vector _elements; 306 | public: 307 | void add(T element) 308 | { 309 | std::lock_guard locker(_lock); 310 | _elements.push_back(element); 311 | } 312 | 313 | void addrange(int num, ...) 314 | { 315 | va_list arguments; 316 | 317 | va_start(arguments, num); 318 | 319 | for (int i = 0; i < num; i++) 320 | { 321 | std::lock_guard locker(_lock); 322 | add(va_arg(arguments, T)); 323 | } 324 | 325 | va_end(arguments); 326 | } 327 | 328 | void dump() 329 | { 330 | std::lock_guard locker(_lock); 331 | for(auto e : _elements) 332 | std::cout << e << std::endl; 333 | } 334 | }; 335 | 336 | 337 | 虽然 `dump()` 方法应该声明为 const , 因为它并没有修改容器的状态。但是如果你声明为 const 以后,编译器会报如下错误: 338 | 339 | ‘std::lock_guard<_Mutex>::lock_guard(_Mutex &)' : cannot convert parameter 1 from ‘const std::recursive_mutex' to ‘std::recursive_mutex &' 340 | 341 | 一个 mutex(regardless which implement is used) 必须被请求和释放,实现上调用了非常量的函数 `lock()` 和 `unlock()` 。因此 `lock_guard` 的参数逻辑上不应该是常量。解决这个问题的方法是使用 `mutable` 声明 mutex 。Mutable 允许在常量方法中使用。 342 | 343 | template 344 | class container 345 | { 346 | mutable std::recursive_mutex _lock; 347 | std::vector _elements; 348 | public: 349 | void dump() const 350 | { 351 | std::lock_guard locker(_lock); 352 | for(auto e : _elements) 353 | std::cout << e << std::endl; 354 | } 355 | }; 356 | 357 | 这些 wrapper guards 的构造函数已经负载(overloads)参数指示锁的策略。可用的策略有: 358 | 359 | + `defer_lock_t` 类型的 `defer_lock` : 不请求 mutex ; 360 | + `try_to_lock_t` 类型的 `try_to_lock` : 试图请求 mutex ,不阻塞 ; 361 | + `adopt_lock_t` 类型的 `adopt_lock` : 用 mutex 唤醒调用线程 ; 362 | 363 | 这些策略的声明像这样: 364 | 365 | struct defer_lock_t { }; 366 | struct try_to_lock_t { }; 367 | struct adopt_lock_t { }; 368 | 369 | constexpr std::defer_lock_t defer_lock = std::defer_lock_t(); 370 | constexpr std::try_to_lock_t try_to_lock = std::try_to_lock_t(); 371 | constexpr std::adopt_lock_t adopt_lock = std::adopt_lock_t(); 372 | 373 | 除了这些 mutex 的包装器之外,标准也提供了几个对一个或者多个 mutex 加锁的方法: 374 | 375 | + [lock](http://en.cppreference.com/w/cpp/thread/lock): 使用避免死锁算法对 mutexes 加锁(通过调用 `locks()`, `try_locks` 和 `unlock()` )。 376 | + [try_lock](http://en.cppreference.com/w/cpp/thread/try_lock): 按照指定的 mutexes 顺序调用 `try_lock()` 尝试调用 mutex 。 377 | 378 | 下面是一个死锁的例子:我们有一个元素容器并且有一个从一个容器和另外一个容器交换的方法 `exchange`。 为了达到线程安全,在两个容器中同步存取,请求不同容器的 mutex 。 379 | 380 | template 381 | class container 382 | { 383 | public: 384 | std::mutex _lock; 385 | std::set _elements; 386 | 387 | void add(T element) 388 | { 389 | _elements.insert(element); 390 | } 391 | 392 | void remove(T element) 393 | { 394 | _elements.erase(element); 395 | } 396 | }; 397 | 398 | void exchange(container& cont1, container& cont2, int value) 399 | { 400 | cont1._lock.lock(); 401 | std::this_thread::sleep_for(std::chrono::seconds(1)); // <-- forces context switch to simulate the deadlock 402 | cont2._lock.lock(); 403 | 404 | cont1.remove(value); 405 | cont2.add(value); 406 | 407 | cont1._lock.unlock(); 408 | cont2._lock.unlock(); 409 | } 410 | 411 | 假定这个函数被不同的线程访问,从 容器1 中移除一个元素添加到 容器2 中;然后把移除 容器2 中的元素添加到 容器1 中。这样会导致死锁(如果线程上下文仅仅在第一次请求的时候从一个线程到另外一个线程切换)。 412 | 413 | int main() 414 | { 415 | srand((unsigned int)time(NULL)); 416 | 417 | container cont1; 418 | cont1.add(1); 419 | cont1.add(2); 420 | cont1.add(3); 421 | 422 | container cont2; 423 | cont2.add(4); 424 | cont2.add(5); 425 | cont2.add(6); 426 | 427 | std::thread t1(exchange, std::ref(cont1), std::ref(cont2), 3); 428 | std::thread t2(exchange, std::ref(cont2), std::ref(cont1), 6); 429 | 430 | t1.join(); 431 | t2.join(); 432 | 433 | return 0; 434 | } 435 | 436 | 为了修正这个问题,你可以使用 `std::lock` 保证在 deadlock-free 的方式下请求 mutex : 437 | 438 | void exchange(container& cont1, container& cont2, int value) 439 | { 440 | std::lock(cont1._lock, cont2._lock); 441 | 442 | cont1.remove(value); 443 | cont2.add(value); 444 | 445 | cont1._lock.unlock(); 446 | cont2._lock.unlock(); 447 | } 448 | 449 | ## 条件变量(Condition variables) ## 450 | 451 | 452 | ---- 453 | 454 | ## 扩展资料 ## 455 | 456 | + [C++11 threads, locks and condition variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables) 457 | + [mutable Data Members (C++)](http://msdn.microsoft.com/en-us/library/4h2h0ktk.aspx) 458 | + [C++'s mutable and conceptual constness](http://www.highprogrammer.com/alan/rants/mutable.html) 459 | -------------------------------------------------------------------------------- /posts/统一的初始化方式.md: -------------------------------------------------------------------------------- 1 | Uniform-Initialization|Language|2013-07-13 2 | 3 | 1. 本文来自:[C++11 FAQ中文版:统一初始化的语法和语义](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-unified-initialization-syntax-and-semantics.html) 4 | 2. 暂无测试代码 5 | 6 | C++03 的类型初始化有多种方式,因此也引来很多问题,如果错用的时候,错误的调试信息会非常模糊。 7 | 8 | 比如以下初始化变量的方式: 9 | 10 | std::string a[] = {"Hello", "world"}; // 正确:初始化数组变量 11 | // 错误:初始化列表引用在非聚合的向量上 12 | std::vector v = {"hello", "world"}; 13 | void f(string a[]); 14 | f ({"foo", "bar"}); // 语法错误,把一个块(block)作为了参数 15 | int a = 2; // "赋值风格"的变化 16 | int aa[] = {2, 3}; // 用初始化列表进行的赋值风格的初始化 17 | complex z(1, 2); // 函数风格的初始化 18 | x = Ptr(y); // "函数风格" 转换/赋值/构造操作 19 | 20 | 21 | 所谓统一的初始化方式,就是把之前 C++ 风格的各种初始化对象方式,统一为一种方式(语法): 22 | 23 | X x1 = X{1, 2}; 24 | X x2 = {1, 2}; 25 | X x3{1, 2}; 26 | X * p = new X{1, 2}; 27 | 28 | struct D : X { 29 | D(int x, int y) : X{x, y} {/* ... */} 30 | } 31 | 32 | struct S { 33 | int a[3]; 34 | S(int x, int y, int z) : a{x, y, z} {/* ... */} 35 | } 36 | 37 | 重点在于: X{a}在所有的可执行代码中都创建了同一个相同的值,所以使用"{}"进行的初始化,在合法的使用的时候,在任何地方都产生相同的结果。例如: 38 | 39 | X x{a}; 40 | X* p = new X{a}; 41 | z = X{a}; 42 | f({a}); 43 | return {a}; 44 | 45 | 46 | ## 扩展资料 ## 47 | 48 | + [C++11 FAQ中文版:统一初始化的语法和语义](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-unified-initialization-syntax-and-semantics.html) 49 | 50 | -------------------------------------------------------------------------------- /posts/静态断言.md: -------------------------------------------------------------------------------- 1 | Static-Assert|Language|2013-07-13 2 | 3 | C++03 提供了方法去测试断言:使用宏和预处理指令 `#error` 。然而,这并不适合模板:宏测试断言是在运行时确定的,预处理指令是在是在预处理期,它们都发生在模板实例化之前。 4 | 5 | 新功能引入了一种新的测试断言方法,在编译期确定。使用新的关键字 `static_assert`。如下声明: 6 | 7 | static_assert (constant-expression, error-message); 8 | 9 | eg: 10 | 11 | static_assert((PI > 3.14) && (PI < 3.15), "PI is inaccurate"); 12 | 13 | template 14 | struct Check { 15 | static_assert(sizeof(int) <= sizeof(T), "T is too big enough!"); 16 | }; 17 | 18 | template 19 | Integral foo(Integral x, Integral y) { 20 | static_assert(std::is_integral::value, "foo() parameter must be an integral type"); 21 | } 22 | 23 | 当常量表达式为 `false` 时,编译器会生成一条错误信息。第一个例子有点像预处理宏`#error`,尽管与处理器只支持整数类型。对于而言,第二个例子在模板类 `Check` 实例化的时候进行检查。 24 | 25 | 静态断言在模版之外也同样很有用。比如特殊算法可能依赖于 `long long int`,要比 `int` 大很多。 26 | -------------------------------------------------------------------------------- /tools/ConvertPostsToJekyllPosts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | 6 | def get_posts_name_and_path(dir_path): 7 | posts_name = [] 8 | posts_path = [] 9 | for file_name in os.listdir(dir_path): 10 | file_path = os.path.join(dir_path, file_name) 11 | posts_name.append(file_name) 12 | posts_path.append(file_path) 13 | 14 | return posts_name, posts_path 15 | 16 | 17 | def split_header_and_content(file_path): 18 | try: 19 | file_handle = open(file_path, 'r') 20 | except IOError: 21 | print '%s open failure' % file_path 22 | return 23 | 24 | header = file_handle.readline().strip() 25 | content = file_handle.read() 26 | 27 | file_handle.close() 28 | 29 | return header, content 30 | 31 | def parse_header(header): 32 | print header 33 | header_fields = header.split('|') 34 | 35 | url = header_fields[0] 36 | category = header_fields[1] 37 | date = header_fields[2] 38 | 39 | if not category: 40 | print 'empty category' 41 | 42 | return url, category, date 43 | 44 | def get_yaml_title(file_name): 45 | return file_name.replace('.md', '') 46 | 47 | def generate_yaml_header(title, category): 48 | yaml_header = '---\n' 49 | yaml_header += 'layout : post\n' 50 | yaml_header += 'title : \"%s\"\n' % title 51 | yaml_header += 'category : \"%s\"\n' % category 52 | yaml_header += '---\n' 53 | 54 | return yaml_header 55 | 56 | def generate_yaml_file_name(based_url, date): 57 | return date + '-' + based_url + '.md' 58 | 59 | def generate_complete_yaml_file(file_full_path, yaml_header, content): 60 | try: 61 | file_handle = open(file_full_path, 'w') 62 | except IOError: 63 | print '%s open failure' % file_full_path 64 | return 65 | 66 | file_handle.write(yaml_header) 67 | file_handle.write(content) 68 | 69 | file_handle.close() 70 | 71 | 72 | def convert_one_to_yaml(old_name, old_path, new_dir): 73 | print old_name, old_path 74 | title = get_yaml_title(old_name) 75 | header, content = split_header_and_content(old_path) 76 | url, category, date = parse_header(header) 77 | 78 | yaml_header = generate_yaml_header(title, category) 79 | yaml_file_name = generate_yaml_file_name(url, date) 80 | 81 | full_path = os.path.join(new_dir, yaml_file_name) 82 | generate_complete_yaml_file(full_path, yaml_header, content) 83 | 84 | def main(): 85 | new_dir = './_posts' 86 | if not os.path.exists(new_dir): 87 | os.mkdir(new_dir) 88 | 89 | posts_name, posts_path = get_posts_name_and_path('../posts') 90 | 91 | for i in range(0, len(posts_name)): 92 | print 'convert process %d/%d' % (i+1, len(posts_path)) 93 | convert_one_to_yaml(posts_name[i], posts_path[i], './_posts') 94 | 95 | 96 | if __name__ == '__main__': 97 | main() 98 | -------------------------------------------------------------------------------- /tools/RenameCategory.py: -------------------------------------------------------------------------------- 1 | #/usr/bin/env python 2 | 3 | import os 4 | 5 | def get_posts_name_and_path(dir_path): 6 | posts_path = [] 7 | for file_name in os.listdir(dir_path): 8 | file_path = os.path.join(dir_path, file_name) 9 | posts_path.append(file_path) 10 | 11 | return posts_path 12 | 13 | def rename_category(file_path, old, new): 14 | file_in = open(file_path, 'r') 15 | content = [] 16 | while True: 17 | line = file_in.readline() 18 | if not line: 19 | break 20 | content.append(line) 21 | file_in.close() 22 | 23 | if content[0].split('|')[1] == old: 24 | content[0] = content[0].replace(old, new) 25 | print content[0] 26 | 27 | file_out = open(file_path, 'w') 28 | for line in content: 29 | file_out.write(line) 30 | file_out.close() 31 | 32 | def main(): 33 | posts_path = get_posts_name_and_path('../posts') 34 | for item in posts_path: 35 | rename_category(item, 'Document', 'Language') 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Alternative-Function-Syntax.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "新的函数声明语法" 4 | category : "Language" 5 | --- 6 | 7 | 译自 [Alternative function syntax](https://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax) 8 | 9 | 10 | 标准 C 函数声明语法对于 C 语言的特性来说是非常完美的。C++ 是从 C 语言扩展来而的,它保持了 C 的基本语言并且进行了一些必需的扩展。然后,随着 C++ 的语言复杂化,它有了很多的局限。特别是在模板函数的声明。下面的例子在 C++03 中是不允许的: 11 | 12 | template 13 | Ret adding_func(const Lhs & lhs, const Rhs & rhs) { return lhs + rhs; } // Ret must be the type of lhs+rhs 14 | 15 | 因为 Ret 的类型是由 lhs 和 rhs 共同决定的(比如 lhs 是 char , rhs 是 int,返回的应该是个 int),尽管 C++11 有了 `decltype` 可以进行类型推导,但是下面的使用也是不可以的: 16 | 17 | template 18 | decltype(lsh+rhs) adding_func(const Lhs & lhs, const Rhs & rhs) { return lhs + rhs; } // Not legal C++11 19 | 20 | 不合法的原因是 lhs 和 rhs 在函数定义之前出现了,直到编译器解析到函数原型的后半部分, lhs 和 rhs 才是有意义的。 21 | 22 | 为了解决这个问题,C++11 引入了一种新的函数声明语法,即`尾部返回值(trailing-return-type)`: 23 | 24 | template 25 | auto adding_func(const Lhs & lhs, const Rhs & rhs) -> decltype(lhs+rhs) { return lhs + rhs; } 26 | 27 | 这种语法可以应用到很多普通函数的声明和定义: 28 | 29 | struct SomeStruct { 30 | auto func_name(int x, int y) -> int; 31 | }; 32 | 33 | auto SomeStruct::func_name(int x, int y) -> int { 34 | return x + y; 35 | } 36 | 37 | 此种用法实际与lambda函数是很统一的,并不是很莫名其妙的语法。 38 | 39 | 40 | 41 | ## 扩展资料 ## 42 | 43 | + [Alternative function syntax](https://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax) 44 | + [Improved Type Inference in C++11: auto, decltype, and the new function declaration syntax](http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html) 45 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Control-and-query-object-alignment.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "控制和查询对象布局" 4 | category : "Language" 5 | --- 6 | 7 | + 内容为个人理解,不保证正确。 8 | 9 | 10 | C++11 可以显示的控制和查询内存布局方式。 11 | 12 | 通过使用 `alignof` 可以得到对象的内存布局字节基数,比如: 13 | 14 | struct char_align { 15 | char c1; 16 | char c2; 17 | }; 18 | 19 | `alignof(char_align)` 得到的值为 `1`. 20 | 21 | struct short_int_align { 22 | short int si1; 23 | short int si2; 24 | }; 25 | 26 | `alignof(short_int_align)` 得到的值 `2` . 27 | 28 | struct test_align { 29 | int i; 30 | char c; 31 | float f; 32 | double d; 33 | long int li; 34 | }; 35 | 36 | `alignof(short_int_align)` 得到的值是 `4`. 37 | 38 | 如果之前了解过 C/C++ 字节对齐的朋友应该一点就明白了,我这里不解释了,不明白的话,建议去看一下字节对齐这个技术。 39 | 40 | `alignas` 与之相反,可以显式的指定字节对其方式,指定的参数必须是 2 的倍数。eg: 41 | 42 | struct alignas(8) test_alignas { 43 | int i; 44 | char c; 45 | float f; 46 | double d; 47 | long int li; 48 | }; 49 | 50 | 这么做指定了 test_alignas 按照 8 字节对齐。再使用 `alignof(test_alignas)` 得到的值就是 8 . 51 | 52 | ## 扩展资料 ## 53 | 54 | + [What does static_assert do, and what would you use it for?](http://stackoverflow.com/questions/1647895/what-does-static-assert-do-and-what-would-you-use-it-for) 55 | + [Control and query object alignment](https://en.wikipedia.org/wiki/C%2B%2B11#Control_and_query_object_alignment) 56 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Explicit-Overrides-And-Final.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "显式重载和 Final 操作符" 4 | category : "Language" 5 | --- 6 | 7 | 译自: [Explicit overrides and final](https://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final) 8 | 9 | 在 C++03 中,重载基类的虚函数,不能达到重载的效果。比如: 10 | 11 | struct Base { 12 | virtual void some_func(float a) 13 | { 14 | std::cout << "Base: " << a << std::endl; 15 | } 16 | }; 17 | 18 | struct Derived : Base { 19 | virtual void some_func(int a) 20 | { 21 | std::cout << "Derived: " << a << std::endl; 22 | } 23 | }; 24 | 25 | 当用基类指针指向派生类对象时,调用`some_func`并不调用派生类的 `some_func`(也就是上面说的不能达到重载的效果,但是编译器并没有报错)。这是因为它们有不同的函数签名(signature),它们是两个不同的虚函数。常常会因为修改了基类的函数参数,却没有同步派生类的函数参数,引发很多问题(多态)。 26 | 27 | C++11 提供显式重载语法来解决这个问题: 28 | 29 | struct NewBase { 30 | virtual void some_func(float a) 31 | { 32 | std::cout << "NewBase: " << a << std::endl; 33 | } 34 | }; 35 | 36 | struct NewDerived : NewBase { 37 | virtual void some_func(int a) override // ill-formed - doesn't override a base class method 38 | { 39 | std::cout << "NewDerived: " << a << std::endl; 40 | } 41 | }; 42 | 43 | 显示的使用`override`告诉编译器检查基类(们)去检查是否有虚函数有确定的签名,如果没有的话,编译器会报错。(译者住:这样的话,相当与把问题放大了,让用户明确自己的使用)。 44 | 45 | C++11 也提供了阻止派生类重载基类函数的方法,是用`final`来实现的: 46 | 47 | struct Base1 final{}; 48 | 49 | struct Derived : Base final {}; // ill-formed because the class Base1 has been marked final 50 | 51 | struct Base2 { 52 | virtual void f() final; 53 | }; 54 | 55 | struct Derived2 : Base2 { 56 | virtual void f(); // ill-formed because the class Base1 has been marked final 57 | }; 58 | 59 | `Base1`是`final`类,`virtual void f() final`是`final`函数,显示告诉编译器该类/虚函数是不能用来派生/重载的, 60 | 61 | 注意,不管是`override`还是`final`都不是语言的关键字,它们是技术上的标志符(`technically identifiers`);只有在指定的上下文中才有特定的含义。在其他情况下,它们都是无意义的标志符。 62 | 63 | ## 扩展资料 ## 64 | 65 | + [Explicit overrides and final](https://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final) 66 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Explicitly-Defaulted-and-Deleted-Special-Member-Functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "显式使用或禁用特定的成员函数" 4 | category : "Language" 5 | --- 6 | 7 | 译自:[Explicitly defaulted and deleted special member functions](https://en.wikipedia.org/wiki/C%2B%2B11#Explicitly_defaulted_and_deleted_special_member_functions) 8 | 9 | 在 C++03 中,编译器为类提供了默认的构造函数,拷贝构造函数,赋值运算符(operator=),析构函数。程序员通过自己定义这些函数来重载他们,C++ 也定义了几个全局运算符(operator=,operator new)可供类来使用,程序员也可以重载它们。 10 | 11 | 然而,这种可控性太弱了。让一个类天生就不可复制,我们可以声明一个 `private` 的拷贝构造函数和赋值运算符,但是不实现它们。使用它们的时候,会产生一个链接错误。 12 | 13 | 在这种情况下,如果类中定义了任何一种构造函数,编译器不再生成默认的构造函数。很多情况下,这样做是很有用的。但是,如果一个类既在指定了构造函数的同时,编译器又可以生成一个默认的构造函数就更好了。 14 | C++ 允许显式的使用或者删除指定的成员函数。例如,下面声明了使用中的默认构造函数: 15 | 16 | struct SomeType { 17 | SomeType() = default; 18 | SomeType(OtherType value); 19 | }; 20 | 21 | 下面的类是不可复制的: 22 | 23 | struct NonCopyable { 24 | NonCopyable() = default; 25 | NonCopyable(const NonCopyable&) = delete; 26 | NonCopyable & operator=(const NonCopyable&) = delete; 27 | }; 28 | 29 | `= delete` 表示这个函数的不可调用的,也可以用这种方法来禁止函数传入特定的参数: 30 | 31 | struct NoInt { 32 | void f(double i); 33 | void f(int) = delete; 34 | }; 35 | 36 | 编译器不允许 `NoInt::f` 传入一个 `int` 类型的参数,而不是悄悄的把 `int` 转成 `double`。下面这种方法可以禁掉除 `double` 之外的所有参数: 37 | 38 | struct OnlyDouble { 39 | void f(double d); 40 | template void f(T) = delete; 41 | }; 42 | 43 | ## 扩展资料 ## 44 | 45 | + [Explicitly defaulted and deleted special member functions](https://en.wikipedia.org/wiki/C%2B%2B11#Explicitly_defaulted_and_deleted_special_member_functions) 46 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Extern-Template.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "外部模板" 4 | category : "Language" 5 | --- 6 | 7 | 在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化(instantiate)。这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化。 8 | 9 | 原 C++ 强制编译器在特定位置开始实例化的语法: 10 | 11 | template class std::vector; 12 | 13 | C++11 引入外部模板这一概念,将强制编译器在特定位置开始实例化,使用 extern 来显式组织编译器在某个编译单元内实例化。使用如下代码: 14 | 15 | extern template class std::vector; 16 | 17 | 这样告诉编译器不要在该编译单元内将该模板实例化(因为已经在别的单元实例化了)。 18 | 19 | 20 | 以上来自[外部模板](http://zh.wikipedia.org/wiki/C%2B%2B11#.E5.A4.96.E9.83.A8.E6.A8.A1.E6.9D.BF),我对语言稍作组织。另,这个概念比较好理解,就不再举例说明了。 21 | 22 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Initializer-lists.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "初始化列表" 4 | category : "Language" 5 | --- 6 | 7 | 译自:[https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists](https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists),非全部翻译,进行了一定调整和扩充。 8 | 9 | 10 | C++03 的初始化列表特性是从 C 语言继承过来的,结构体或者数组可以用大括号包含的参数列表来初始化(按照定义的顺序依次初始化)。这种初始化方式递归的,因此结构体数组或者结构体包含其他结构体也可以这么用。 11 | 12 | struct object 13 | { 14 | float first; 15 | int second; 16 | }; 17 | object scalar = {0.43f, 10}; 18 | object an_array[3] = { {13.4f, 3}, {43.28f, 29}, {5.934f, 17} }; 19 | 20 | C++03 的初始化列表特性,只能用于结构体或者符合POD的类。C++11 对这个特性进行了扩展,可以用户有所的类包括标准库容器,比如 `std::vector`。 21 | 22 | C++11 使用模板`std::initializer_list`来实现使用列表初始化这一技术,构造函数或者其他函数都可以把它作为参数。比如: 23 | 24 | template 25 | class my_list 26 | { 27 | public: 28 | my_list() {} 29 | 30 | my_list(const std::initializer_list & x) { 31 | for (auto ite=x.begin(); ite!=x.end(); ++ite) { 32 | items_.push_back(*ite); 33 | } 34 | } 35 | void show_list() { 36 | copy(items_.begin(), items_.end(), std::ostream_iterator(std::cout, " ")); 37 | std::cout << std::endl; 38 | } 39 | private: 40 | std::vector items_; 41 | }; 42 | 43 | 对于一个函数: 44 | 45 | void print_init_list(const std::initializer_list x) { 46 | copy(x.begin(), x.end(), std::ostream_iterator(std::cout, " ")); 47 | std::cout << std::endl; 48 | } 49 | 50 | 值得注意的是,需要使用`{}`来初始化一个 `std::initializer_list` 对象,比如上面的函数调用方式为 `print_init_list({5, 4, 3, 2, 1})`。列表只能被构造和复制一次,初始化列表是常量,不要妄图去修改它的值。 51 | 52 | C++11 标准容器也可以用以下的方式去初始化: 53 | 54 | std::list test_list = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 55 | std::vector test_string = {"Make", "One", "Program", "Do", "One", "Thing", "Well"}; 56 | 57 | 58 | ## 扩展资料 ## 59 | 60 | + [Initializer lists](https://en.wikipedia.org/wiki/C%2B%2B11#Initializer_lists) 61 | + [C++11: Initializer lists](http://oopscenities.net/2011/05/09/c0x-initializer-lists/) 62 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Install-GCC4.8-Ubuntu.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "Install GCC4.8 In Ubuntu 12.04" 4 | category : "Compiler" 5 | --- 6 | 7 | ## 安装 ## 8 | 9 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 10 | sudo apt-get update 11 | sudo apt-get install gcc-4.8 12 | sudo apt-get install g++-4.8 13 | 14 | ## 测试 ## 15 | 16 | 方法一: 17 | 18 | gcc-4.8 -v 19 | g++-4.8 -v 20 | 21 | 如果安装成功应输出为:gcc version 4.8.1 (Ubuntu 4.8.1-2ubuntu1~12.04) 22 | 23 | 方法二: 24 | 25 | ls /usr/bin/gcc* -l 26 | ls /usr/bin/g++* -l 27 | 28 | 我的输出结果为: 29 | 30 | -rwxr-xr-x 1 root root 306200 4月 15 08:27 /usr/bin/gcc-4.6 31 | -rwxr-xr-x 1 root root 527596 4月 22 04:47 /usr/bin/gcc-4.7 32 | -rwxr-xr-x 1 root root 716296 6月 5 22:00 /usr/bin/gcc-4.8 33 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-ar-4.7 34 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-ar-4.8 35 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-nm-4.7 36 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-nm-4.8 37 | -rwxr-xr-x 1 root root 21988 4月 22 04:47 /usr/bin/gcc-ranlib-4.7 38 | -rwxr-xr-x 1 root root 22000 6月 5 22:00 /usr/bin/gcc-ranlib-4.8 39 | 40 | Ubuntu 默认 GCC 版本是4.6,我之前曾经装过 4.7,现在装了 4.8,所以你可以看到三个版本。 41 | 42 | ## 将 4.8 设为默认 GCC 版本 ## 43 | 44 | 为了修改默认 GCC 版本,我们需要修改软链接。当然,你也可以不修改,只不过每次使用的时候都需要 gcc-4.8/g++-4.8 带上版本,不方便。 45 | 46 | sudo rm /usr/bin/gcc 47 | sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc 48 | sudo rm /usr/bin/g++ 49 | sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++ 50 | 51 | 然后, gcc -v 提示的 GCC 版本就是 4.8.1,非常 Happy。 52 | 53 | ## 测试 Demo ## 54 | 55 | 安装了可能感觉不够爽,是时候用 gcc4.8 编一把 C++11 了,才不枉废了半天劲。我找了一个简单的 demo : 56 | 57 | // Ubuntu 12.04 GCC4.8 58 | 59 | #include 60 | #include 61 | 62 | int main() 63 | { 64 | char s[] = "Hello CPP11 World!"; 65 | int Uppercase = 0; 66 | 67 | std::for_each(s, s+sizeof(s)/sizeof(char), [&Uppercase] (char c) 68 | { 69 | if (isupper(c)) 70 | Uppercase++; 71 | }); 72 | 73 | std::cout << Uppercase << " uppercase letters in: " << s << std::endl; 74 | 75 | return 0; 76 | } 77 | 78 | 注意:编译的时候加上`-std=c++11`(不加编译器也会很友善的提醒你,^_^); 79 | 80 | ## 参考资料 ## 81 | 82 | + [Ubuntu12.04安装GCC4.7并设置C++11](http://maykiller.com/2012/ubuntu-install-gcc-4-7-with-set-default/1/),非常感谢这位博主,在我多方尝试后这是一个有效且简单的做法。 83 | + 测试Demo来自:[C++11 中值得关注的极大变化(详解)](http://coolshell.cn/articles/5265.html) 84 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Lambda-Function-And-Expressions.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "Lambda 函数和表达式" 4 | category : "Language" 5 | --- 6 | 7 | C++11 支持匿名函数,文档中称为 `Lambda函数`,lambda 表达式格式如下: 8 | 9 | [capture](parameters)->return-type{body} 10 | 11 | 如果 `parameters`没有值的话,括号可以省略。如果 `body` 只有一个返回状态或者返回值为 void ,`return-type` 也经常被省略掉。如下: 12 | 13 | [capture](parameters){body} 14 | 15 | 一些 lambda 函数举例: 16 | 17 | [](int x, int y) { return x + y; } // implicit return type from 'return' statement 18 | [](int& x) { ++x; } // no return statement -> lambda function's return type is 'void' 19 | []() { ++global_x; } // no parameters, just accessing a global variable 20 | []{ ++global_x; } // the same, so () can be omitted 21 | 22 | `return-type` 不指定类型的时候,C++11 使用 decltype 来解析返回值类型(比如 `decltype(x+y)`)。 23 | 24 | 返回类型可以显式的用如下的方式指定: 25 | 26 | [](int x, int y) -> int { int z = x + y; return z; } 27 | 28 | lambda 函数可以使用 lambda 函数外面的标志符。这些变量的集合通常被成为[闭包](https://en.wikipedia.org/wiki/Closure_(computer_science)#Function_objects_.28C.2B.2B.29),闭包在 lambda 表达式的 `[]` 中定义,允许是值或者引用。如下所示: 29 | 30 | [] //no variables defined. Attempting to use any external variables in the lambda is an error. 31 | [x, &y] //x is captured by value, y is captured by reference 32 | [&] //any external variable is implicitly captured by reference if used 33 | [=] //any external variable is implicitly captured by value if used 34 | [&, x] //x is explicitly captured by value. Other variables will be captured by reference 35 | [=, &z] //z is explicitly captured by reference. Other variables will be captured by value 36 | 37 | 下面的两个例子演示了 lambda 表达式的用法: 38 | 39 | std::vector some_list{ 1, 2, 3, 4, 5 }; 40 | int total = 0; 41 | std::for_each(begin(some_list), end(some_list), [&total](int x) { 42 | total += x; 43 | }); 44 | 45 | 计算 `some_list` 的加和,存储到 `total` 中(引用传递)。 46 | 47 | std::vector some_list{ 1, 2, 3, 4, 5 }; 48 | int total = 0; 49 | int value = 5; 50 | std::for_each(begin(some_list), end(some_list), [&, value, this](int x) { 51 | total += x * value * this->some_func(); 52 | }); 53 | 54 | 除 value 和 this 外,引用传递。计算得到 total 的值。只能抓取闭包中的非静态函数,lambda 和创建它的时候具有相同的存取权限,不管是否 protected/private 成员。 55 | 56 | 一旦抓取到了,不管是隐式的还是显式(的调用),闭包中类的成员范围是可测的(If this is captured, either explicitly or implicitly, then the scope of the enclosed class members is also tested.)。使用成员的时候不再需要显式的 `this->`语法。 57 | 58 | 如果闭包中引用的局部变量,它创建的更内层块引用之后调用,行为是未定义的。(If a closure object containing references to local variables is invoked after the innermost block scope of its creation, the behaviour is undefined.) 59 | 60 | lambda 函数是实现依赖于类型的函数对象(function object);类型的名字只有编译器可用。如果用户向把 lambda 函数作为参数来用的话,类型必须是一个模板类型,或者他们必须创建一个 `std::function` 或者一个相似的对象去抓取 lambda 值。 auto 关键可以用来存储 lambda 函数。 61 | 62 | auto my_lambda_func = [&](int x) { /*...*/ }; 63 | auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ }); 64 | 65 | 下面的例子存储匿名函数在变量、vectors,和 arrays 中,然后把他们的名字当参数传递使用: 66 | 67 | #include 68 | #include 69 | #include 70 | double eval(std::function f, double x = 2.0){return f(x);} 71 | int main(){ 72 | std::function f0 = [](double x){return 1;}; 73 | auto f1 = [](double x){return x;}; 74 | decltype(f0) fa[3] = {f0,f1,[](double x){return x*x;}}; 75 | std::vector fv = {f0,f1}; 76 | fv.push_back ([](double x){return x*x;}); 77 | for(int i=0;i double_values(const vector & v) 14 | { 15 | vector new_value; 16 | for (auto itr = v.begin(); itr != v.end(); ++itr) 17 | { 18 | new_value.push_back(2 * *itr); 19 | } 20 | 21 | return new_value; 22 | } 23 | 24 | `double_values` 中 `new_value` 的构造是必须的,原则上来说,有两次拷贝:一次是返回 `new_value` 时,会产生一个临时对象,另外一次是在调用 `double_value` 时产生的,比如 `v = double_values(v);`,第一次拷贝由编译器进行一定的优化,而第二次调用 `vector` 的赋值运算符,需要复制所有的数据,也就是需要新的内存,既而迭代拷贝数据,之后临时对象进行析构。 25 | 26 | ## 解决方法 ## 27 | 28 | 接上面的例子。理论上来讲,临时对象,构造->赋值->析构,构造时,申请空间;析构时,释放空间(像是句废话,其实不然)。针对这个语句来说: `v = double_values(v)`,赋值过程中,释放内存->申请内存->赋值。那么有没有一种办法,可以让临时对象和v不要做重复的事情呢?我们假设是这样:把临时对象中的内存直接为v所用,这样就省了很多事情。 29 | 30 | 在 C++11 中,提供了 move constructor 和 move assignment 来解决这种问题。move语义可以减少很多不必须要的临时对象拷贝操作,并且保证从临时对象中拿到的资源是安全的。 31 | 32 | move 语义的实现依赖于`(右值引用)rvalue-reference`。在介绍右值引用之前,先简单介绍一下左值和右值的概念: 33 | 34 | + Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue. 35 | + An lvalue is an expression whose address can be taken, a locator value--essentially, an lvalue provides a (semi)permanent piece of memory. You can make assignments to lvalues, An expression is an rvalue if it results in a temporary object 36 | 37 | ### 右值引用 ### 38 | 39 | 右值引用会和一个临时对象绑定。比如,在 C++11 之前,如果你有一个临时对象,你可以用`regular`或者`lvalue reference` 去绑定它,但是仅仅是在`const`的情况下: 40 | 41 | const string & name = get_name(); // ok 42 | string& name = get_name(); // NOT ok 43 | 44 | 这是因为临时对象生存周期所限,引用它的地址,一直它本身就消失了,这是很危险的。注意,常量引用一个临时对象,这个对象不会立即析构,但是他仍旧是一个临时对象,所以你不能修改它。 45 | 46 | 在 C++11 中,右值引用允许你为右值绑定一个可变引用,但是不能是一个左值。换句话说,右值引用可以检测到一个对象是不是临时对象。右值引用使用`&&`语法来声明而不是`&`,可以是常量,也可以是非常量。和左值引用一样,尽管你很少见到一个常量右值引用。 47 | 48 | const string && name = get_name(); // ok 49 | string && name = get_name(); // alse ok - praise be! 50 | 51 | 使用左值和右值引用重载函数,调用情况是什么样呢? 52 | 53 | void print_ref(const std::string & str) 54 | { 55 | std::cout << str << std::endl; 56 | } 57 | 58 | void print_ref(std::string && str) 59 | { 60 | std::cout << str << std::endl; 61 | } 62 | 63 | std::string name("jerryzhang"); 64 | print_ref(name); // calls the first print_ref function, taking an lvalue reference 65 | print_ref(get_name()); // calls the second print_ref function, taking a mutable rvalue reference 66 | 67 | 现在,我们有了自己的方式去判定一个引用变量指向的是临时对象还是非临时变量(permanent object)。右值引用版本的方法,像是一个俱乐部的秘密后门入口,只有你可以进入,如果你是一个临时对象的话。 68 | 69 | 了解了右值引用,是时候看看它的具体用途了。 70 | 71 | 72 | ### move constructr 和 move assignment operator ### 73 | 74 | 右值引用的最常用用途是创建move构造函数和move赋值运算符(具有相同的规则)。move构造可以避免重新申请内存,因为我们知道它已经提供了一个临时对象,我们不需要从它来复制字段,而是直接从它们上move过来。 75 | 76 | 如果字段是一个内置类型(primitive type), 比如`int`,我们就直接复制。其实我们关心的是,字段的类型是`指针`:这种情况下,不是申请内存/初始化内存,我们可以直接`偷取`指针,然后把临时对象的指针指空。我们知道临时对象已经不再用,因此可以使用它指针所指向的内存。 77 | 78 | 假如我们有下面这样一个简单的`ArrayWrapper`类: 79 | 80 | class ArrayWrapper 81 | { 82 | public: 83 | ArrayWrapper(int n) 84 | : _p_vals(new int[n]) 85 | , _size(n) 86 | {} 87 | // copy constructor 88 | ArrayWrapper(const ArrayWrapper & other) 89 | : _p_vals(new int[other._size)) 90 | , _size(other._size) 91 | { 92 | for (int i = 0; i <_size; ++i) 93 | { 94 | _p_vals[i] = other._p_vals[i]; 95 | 96 | } 97 | } 98 | ~ArrayWrapper() 99 | { 100 | delete [] _p_vals; 101 | 102 | } 103 | private: 104 | int * _p_vals; 105 | int _size; 106 | }; 107 | 108 | 注意,拷贝构造函数既申请了内存又对每个值进行了赋值,这个拷贝操作很耗费时间。下面是使用 move 构造,对效率进行了提升: 109 | 110 | class ArrayWrapper 111 | { 112 | public: 113 | // default constructor produces a moderately size array 114 | ArrayWrapper() 115 | : _p_vals(new int[64]) 116 | , _size(64) 117 | {} 118 | 119 | ArrayWrapper(int n) 120 | : _p_vals(new int[n]) 121 | , _size(n) 122 | {} 123 | 124 | // move constructor 125 | ArrayWrapper(ArrayWrapper && other) 126 | : _p_vals(other._p_vals) 127 | , size(other._size) 128 | { 129 | other._p_vals = NULL; 130 | } 131 | 132 | // copy constructor 133 | ArrayWrapper(const ArrayWrapper& other) 134 | : _p_vals(new int[other._p_vals]) 135 | , _size(other._size) 136 | { 137 | for (int i = 0; i < _size ; ++i) 138 | { 139 | _p_vals[i] = other._p_vals[i]; 140 | } 141 | } 142 | ~ArrayWrapper() 143 | { 144 | delete [] _p_vals; 145 | } 146 | private: 147 | int *_p_vals; 148 | int _size; 149 | }; 150 | 151 | `move constructor` 要比 `copy constructor` 简单且高效的多! 有两点需要注意: 152 | 153 | 1. 参数是`非常量`右值引用 154 | 2. `other._p_vals` 要置为 `NULL` 155 | 156 | 第二点是站在第一点的基础上的,如果传入的是一个`常量`的右值引用,`_p_vals`也不可能置为`NULL`。如果没有进行指针的置空的话,临时对象析构时会把指针所指向的内存进行释放,也就是我们的`move`没有真正的`move`——当我们使用已经释放了的内存,会引发崩溃。 157 | 158 | 再重复一下,只有确定参数是一个临时对象的时候才会去调用 `move constructor`——并且只有临时对象才可以被修改。反过来,如果你有一个函数返回的是临时对象,它会去调用 `copy constructor`而不是`move constructor`,因此你不能这样写代码: 159 | 160 | const ArrayWrapper getArrayWrapper(); // make the move constructor useless, the temporary is const! 161 | 162 | 仍旧有一种情况我们没有考虑到:我们需要赋值的字段是一个对象,例如,想象一下,我们有个媒体数据字段: 163 | 164 | class MetaData 165 | { 166 | public: 167 | MetaData(int size, const std::string & name) 168 | : _name(name) 169 | , _size(size) 170 | {} 171 | 172 | // copy constructor 173 | MetaData(const MetaData & other) 174 | : _name(other._name) 175 | , _size(other._size) 176 | {} 177 | 178 | // move constructor 179 | MetaData(MetaData&& other) 180 | : _name(other.name) 181 | , _size(other._size) 182 | {} 183 | 184 | std::string getName() const 185 | { 186 | return _name; 187 | } 188 | 189 | int getSize() const 190 | { 191 | return _size; 192 | } 193 | private: 194 | std::string _name; 195 | int _size; 196 | }; 197 | 198 | 现在我们的数据有一个`name`字段和`size`字段,我们不得不去修改 `ArrayWrapper`的定义: 199 | 200 | class ArrayWrapper 201 | { 202 | public: 203 | // default constructor produces a moderately sized array 204 | ArrayWrapper() 205 | : _p_vals(new int[64]) 206 | , _metadata(64, "ArrayWrapper") 207 | {} 208 | ArrayWrapper(int n) 209 | : _p_vals(new int[n]) 210 | , _metadata(n, "ArrayWrapper") 211 | {} 212 | 213 | // move constructor 214 | ArrayWrapper(ArrayWrapper && other) 215 | : _p_vals(other._p_vals) 216 | , _metadata(n, "ArrayWrapper") 217 | {} 218 | 219 | // copy constructor 220 | ArrayWrapper(const ArrayWrapper & other) 221 | : _p_vals(new int[other._metadata.getSize()]) 222 | , _metadata(other._metadata) 223 | { 224 | other._p_vals = NULL; 225 | } 226 | 227 | // copy constructor 228 | ArrayWrapper(const ArrayWrapper & other) 229 | : _p_vals(new int[other._metadata.getSize()]) 230 | , _metadata(other._metadata) 231 | { 232 | for (int i = 0; i < _metadata.getSize(); ++i) 233 | { 234 | _p_vals[i] = other._p_vals[i]; 235 | } 236 | } 237 | 238 | ~ArrayWrapper() 239 | { 240 | delete [] _p_vals; 241 | } 242 | 243 | private: 244 | int *_p_vals; 245 | MetaData _metadata; 246 | }; 247 | 248 | 这样可以工作吗?看起来就应该这样实现,不是吗?在 `ArrayWrapper` 的 `move constructor` 中调用 `MetaData` 的 `move constructor`,问题是不能这样执行。原因很简单,在`move constructor`中的`other`值,是一个右值引用?对于`MetaData`,它是一个左值,因此调用`copy constructor`而不是`move constructor`。可以这样去理解,右值是一个即将消失的对象,存活时间有限。我们向`move constructor`中传入一个临时对象,它就有个一个新的作用域,在上下文中右值表达式会被评估,临时对象干完它所做的事情,之后就不存在了。但是在我们的构造中,对象有了一个名字,它可以存活到我们整个函数的作用域,换句话说,我们在函数中使用了临时变量不止一次,这种情况下,临时对象会被定义在本地函数中。实际上,它已经上是本地函数中的一个左值,我们可以使用它的本地地址,和正常的变量一样使用。其实,我们在另外一个函数中使用到了它。如果`MetaData`中我们调用了`move constructor`而不是`copy constructor`,我们直接`move`对象,这样就危险了。 249 | 250 | // move constructor 251 | ArrayWrapper(ArrayWrapper && other) 252 | :_p_vals(other._p_vals) 253 | , _metadata(other._metadata) 254 | { 255 | // if _metadata(other._metadata) calls the move constructor, using 256 | // other._meta here would be extremely dangerous! 257 | other._p_vals = NULL; 258 | } 259 | 260 | 总结:左值和右值引用是左值表达式。不同之处在于左值引用一个左值的常量引用,而右值只是一个右值的引用。有点像指针和它所指对象的区别。指向的是右值,但是,我们用右值自身的时候,它就是一个左值。 261 | 262 | #### std::move #### 263 | 264 | 解决上面问题的办法就是使用 `std::move`,在 `` 中,`std::move` 是这样解释的:`ok, honest to God I know I have an lvalue, but I want it to be an rvalue.`,`std::move` 本身没有做任何移动的操作;它只是把一个左值转换成右值,因此,你可以在我们的 `move constructor` 中调用它来实现转换。我们代码可能会这样去实现: 265 | 266 | #include // for std::move | ps: 实际上,我没有添加这个头文件上也可以使用 std::move 267 | 268 | ArrayWrapper(ArrayWrapper&& other) 269 | :_p_vals(other._p_vals) 270 | , _metadata(std::move(other._metadata)) 271 | { 272 | other._p_vals = NULL; 273 | } 274 | 275 | 当然,我们也应该回到 `MetaData` 把它补充完整: 276 | 277 | MetaData(MetaData&& other) 278 | :_name(std::move(other._name)) // oh, blissful efficiency 279 | : _size(other._size) 280 | {} 281 | 282 | #### Move 赋值运算符 #### 283 | 284 | 我们也应该在 `move assignment operator` 中使用相同的方法来实现 `move construcotr`。 285 | 286 | #### Move构造函数和隐式生成的构造函数 #### 287 | 288 | 我们知道,在C++中,我们声明任何一种构造函数的时候,编译器就不再给你声明默认的构造函数了。这这儿也是这样的,添加一个 `move constructor` 之后,需要你自己声明和定义自己的默认构造函数。 289 | 290 | #### std::move 的工作机制 #### 291 | 292 | 你可能想过,怎么样去写一个像 `std::move` 这样的函数呢?你怎么才能实现把左值引用转换成右值引用呢?可能你已经想到了,是[类型转换](http://www.cprogramming.com/tutorial/lesson11.html)。`std::move`做了很多的调用操作,但是它的核心操作仅仅是使用 `static_cast` 转换成右值引用。也就是说,实际上你不需要使用 `move` —— 你还是要用的,尽管这里面的操作很清晰。事实是这个转换是必要的,这是一个好的习惯!意味着你的转换不会出问题,如果用 `static_cast` 替代 move 是非常危险的。你应该尽可能的使用 `std::move` 去把一个左值转换成右值,确保右值永远不会绑定到自己的左值上。 293 | 294 | ### 显式的返回一个右值引用 ### 295 | 296 | 任何时候你都应该写返回显式右值引用的函数吗?任何时候返回一个右值引用是什么意思?函数返回的对象不已经是右值了吗? 297 | 298 | 先回答第二个问题:返回一个显式的右值引用不同于返回一个对象的值。看下面的例子: 299 | 300 | int x; 301 | 302 | int getInt() 303 | { 304 | return x; 305 | } 306 | 307 | int && getRvalueInt() 308 | { 309 | // notice that it's fine to move a primitive type--remember, std::move is just a cast 310 | return std::move(x); 311 | } 312 | 313 | 第一种情况很清晰,`getInt()` 是一个右值, 是 x 的副本。通过写一些帮助函数,让我们看清这一点: 314 | 315 | void printAddress(const int & v) 316 | { 317 | cout << reinterpret_cast(&v) << endl; 318 | } 319 | 320 | v printAddress(getInt()); 321 | printAddress(x); 322 | 323 | 当你运行这段代码的时候,你会发现打印了两个不同的值。 324 | 325 | 换个方式: 326 | 327 | printAddress(getRValueInt()); 328 | printAddress(x); 329 | 330 | 打印出相同的值,因为我们返回的是一个显示的右值。 331 | 332 | 因此返回一个右值引用和不返回右值引用是不同的,这个不同在你返回一个已经存在对象,而不是临时对象的时候表现的非常明显。 333 | 334 | 现在回到是否必要去做的问题上。答案是,很多情况下不用(probably not)。大多数情况下,它有点像悬挂引用(引用存在,但是临时对象要被析构)。返回左值引用非常危险,可能对象出了作用域就不再存在了。右值引用不能保持一个对象一直存活着。` Returning an rvalue reference would primarily make sense in very rare cases where you have a member function and need to return the result of calling std::move on a field of the class from that function--and how often are you going to do that?` 335 | 336 | ### Move语义和标准库 ### 337 | 338 | 回到最原始的例子上——我们使用`vector`,我们没有控制 `vector` 类是否有 `move constructor` 或者 `move assignment operator`。幸运的是,标准委员会很明智,move 语义已经被添加到标准库中。也就意味着你现在可以高效的返回 `vectors`, `maps`, `strings`和其他标准库中的对象,好好享受 move 语义吧。 339 | 340 | ### STL容器移动 ### 341 | 342 | 实际上,标准库跟近了一步,如果在你自己实现的类中实现 move 语义,当你使用这些类对象的STL容器的时候,STL会自动使用 `std::move`,自动选择最有利的 `move-enable` 类和高效的复制操作。 343 | 344 | ## 扩展资料 ## 345 | 346 | + [C/C++中的左值](http://www.caole.net/diary/lvalue.html) 347 | + [L-Value and R-Value Expressions](http://msdn.microsoft.com/en-us/library/bkbs2cds.aspx) 348 | + [C++11 FAQ中文版:右值引用](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-an-an-rvalue-references.html) 349 | + [Move semantics and rvalue references in C++11](http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html) 350 | + [typecasting](http://www.cprogramming.com/tutorial/lesson11.html) 351 | + [static_cast](http://www.cprogramming.com/reference/typecasting/staticcast.html) 352 | 353 | 354 | ## 贡献者 ## 355 | 356 | + JerryZhang 357 | + Chengjie Qi 358 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Range-based-For-Loop.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "基于范围的for循环" 4 | category : "Language" 5 | --- 6 | 7 | 译自 [Range-based for loop](https://en.wikipedia.org/wiki/C%2B%2B11#Range-based_for_loop) 8 | 9 | 10 | C++11 扩展一种新的遍历列表的 for 循环语法(range-base for,基于范围的 for 循环),使得遍历列表非常简单了。可以遍历 C 式的数组、初始化列表(initializer lists),和定义了 begin()/end() 函数(返回迭代器)的类型。所有的标准库容器都可以使用 range-base for 来遍历: 11 | 12 | std::vector v; 13 | v.push_back(1); 14 | v.push_back(2); 15 | v.push_back(3); 16 | v.push_back(4); 17 | v.push_back(5); 18 | v.push_back(6); 19 | 20 | for (int i : v) { 21 | std::cout << i << " " ; 22 | } 23 | std::cout << std::endl; 24 | 25 | for (auto ite : v) { 26 | std::cout << ite << " "; 27 | } 28 | std::cout << std::endl; 29 | 30 | 31 | for (int& i : v) { 32 | ++i; 33 | } 34 | 35 | 36 | for (auto i : v) { 37 | std::cout << i << " "; 38 | } 39 | std::cout << std::endl; 40 | 41 | 42 | ## 扩展资料 ## 43 | + [C++11 range-based for loops](http://www.cprogramming.com/c++11/c++11-ranged-for-loop.html) 推荐阅读 44 | + [Range-based for loop](https://en.wikipedia.org/wiki/C%2B%2B11#Range-based_for_loop) 45 | 46 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Right-Angle-Bracket.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "右尖括号" 4 | category : "Language" 5 | --- 6 | 7 | 译自:[Right angle bracket](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 8 | 9 | C++03 的析取符定义为">>"(注,在我的第一本C++书中,">>"被称为析取运算符,而"<<"被称为插入运算符,我感觉那样理解挺不错的)。 然而,在模板的嵌套声明中,程序员需要在两个尖括号之间加上一个空格,否则会引发一个编译错误。 10 | 11 | C++11 提供了新的解析规则,多个右尖括号的时候,会优先为模板解析。但是有括号的时候,会先解析括号: 12 | 13 | template class SomeType; 14 | std::vector2>> x1; // Interpreted as a std::vector of SomeType 2>, 15 | // which is not legal syntax. 1 is true. 16 | std::vector2)>> x1; // Interpreted as a std::vector of SomeType, 17 | // which is legal C++11 syntax, (1>2) is false. 18 | 19 | ## 扩展资料 ## 20 | 21 | + [Right angle bracket](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 22 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Static-Assert.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "静态断言" 4 | category : "Language" 5 | --- 6 | 7 | C++03 提供了方法去测试断言:使用宏和预处理指令 `#error` 。然而,这并不适合模板:宏测试断言是在运行时确定的,预处理指令是在是在预处理期,它们都发生在模板实例化之前。 8 | 9 | 新功能引入了一种新的测试断言方法,在编译期确定。使用新的关键字 `static_assert`。如下声明: 10 | 11 | static_assert (constant-expression, error-message); 12 | 13 | eg: 14 | 15 | static_assert((PI > 3.14) && (PI < 3.15), "PI is inaccurate"); 16 | 17 | template 18 | struct Check { 19 | static_assert(sizeof(int) <= sizeof(T), "T is too big enough!"); 20 | }; 21 | 22 | template 23 | Integral foo(Integral x, Integral y) { 24 | static_assert(std::is_integral::value, "foo() parameter must be an integral type"); 25 | } 26 | 27 | 当常量表达式为 `false` 时,编译器会生成一条错误信息。第一个例子有点像预处理宏`#error`,尽管与处理器只支持整数类型。对于而言,第二个例子在模板类 `Check` 实例化的时候进行检查。 28 | 29 | 静态断言在模版之外也同样很有用。比如特殊算法可能依赖于 `long long int`,要比 `int` 大很多。 30 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Strong-Type-Enum.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "强类型枚举" 4 | category : "Language" 5 | --- 6 | 7 | 译自: [Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint](http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html); 8 | 9 | C++03 的枚举本质上就是整型,它们可以和整型,和其它的枚举类型做比较。实际上,你并不希望不同枚举之间进行比较,就好像你不希望把钉子的种类和牙刷的种类做比较一样。 10 | 11 | 旧式枚举的另外一个限制是枚举的值是没有作用域的,换句话说,你不能两个枚举共享同一个名字,比如: 12 | 13 | enum Color {RED, GREEN, BLUE}; 14 | enum Feeling {EXCITED, MOODY, BLUE}; // error: redeclaration of ‘BLUE’ 15 | 16 | C++11 提供了强类型枚举——枚举类(enum classes),如下方式声明: 17 | 18 | `class`意味着每一个枚举类型实际上都是不一样的,不能和其它类型做比较。强类型枚举,枚举类有很好的作用域。每个枚举值作用域被限制在枚举类中。这意味着,想要存取枚举值,你必须要这么用: 19 | 20 | Color color = Color::GREEN; 21 | if (Color::RED == color) { 22 | // the color is red; 23 | } 24 | 25 | 如果你想用旧式的枚举,仍旧是可用的。很大程度上,是可以和既有的代码相兼容的。教你一招,现在你可以直接在值前面加上枚举名:Color::RED.但是这种情况下,不能解决命名冲突的问题,它只是更清晰了一些。 26 | 27 | 枚举类比旧式枚举类型另外一个优势是,你可以使用前向声明一个强枚举类型,意味着你可以这样写代码: 28 | 29 | enum class Mood; 30 | void assessMode (Mood m); 31 | enum class {EXCITED, MOODY, BLUE}; 32 | 33 | Why would this be useful? Forward declarations are often about the physical layout of code on disk into different files or to provide opaque objects as part of an API. In the first case, where you care about the physical disk layout, using a forward declaration allows you to declare an enum type in the header file while putting specific values into the cpp file. This lets you change the list of possible enum values quite frequently without forcing all dependent files to recompile. In the second case, an enum class can be exposed as a type-safe but otherwise opaque value returned from one API function to be passed into another API function. The code using the API need not know the possible values the type can take on. Since the compiler still knows about the type, it can enforce that variables declared to work with that type are not confused with variables working with another type. (译者注:这段话不太理解,所有没有翻译,欢迎补充翻译。) 34 | 35 | 枚举类的最后一个好处是你可以你的枚举的大小,你可以使用任何签名的或者未签名的整型类。默认是 int ,但是你也可以使用 char, unsigned long 等等。这样可以确保跨平台编译器的兼容性。 36 | 37 | enum class Colors : char { RED = 1, GREEN = 2, BLUE = 3 }; 38 | 39 | 但是在在 C++11 中,在指定类型大小上,我们可以用更好的方式,使用 `cstdint`。 40 | 41 | C++ 在类型方面存在一个问题,比如说,你想要一个 32位 的整型,但是 int 可能在不同体系架构下有不同的大小。在 C++11 中,C99 的头文件 `stdint.h` 包含在 `cstdint` 中。`cstdint` 头文件包含了诸如 `std::int8_t`, `std::int32_t` 和 `int64_t`(对应的 unsigned 版本是从 `std::uint8_t` 开始的)的类型。 42 | 43 | 下面是使用新类型和枚举类完美配合,告诉跨平台编译器和体系架构你的枚举大小: 44 | 45 | enum class Colors: std::int8_t { RED = 1, GREEN = 2, BLUE = 3}; 46 | 47 | ## 扩展资料 ## 48 | 49 | + [Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint](http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html) 50 | + [Strongly typed enumerations](https://en.wikipedia.org/wiki/C%2B%2B11#Strongly_typed_enumerations) 51 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Thread.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "线程" 4 | category : "Language" 5 | --- 6 | 7 | 译自:[C++11 threads, locks and condition variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables)。看到如此好文,原谅我的情不自禁,向原作者致谢! 8 | 9 | 10 | ---- 11 | 12 | ## 线程(Threads) ## 13 | 14 | [std::thread](http://en.cppreference.com/w/cpp/thread/thread) 类代表一个可执行的线程,在 `` 下。`std::thread` 可以和普通函数,lambdas 函数,仿函数(实现了 `opertor()` 的类)一起工作。此外,它允许你为你的线程函数传入任意数量的参数。 15 | 16 | #include 17 | 18 | void func() 19 | { 20 | // do some work 21 | } 22 | 23 | int main() 24 | { 25 | std::thread t(func); 26 | t.join(); 27 | 28 | return 0; 29 | } 30 | 31 | `t` 是一个执行 `func` 的线程对象,`join` 阻塞调用线程(这里是住线程),直到该线程运行结束。线程函数的返回值将被忽略的,但是,线程函数可以传入任意数量的参数。 32 | 33 | void func(int i, double d, const std::string& s) 34 | { 35 | std::cout << i << ", " << d << ", " << s << std::endl; 36 | } 37 | 38 | 39 | int main() 40 | { 41 | std::thread t(func, 1, 12.50, "sample"); 42 | t.join(); 43 | 44 | return 0; 45 | } 46 | 47 | 48 | 虽然可以给线程函数传递任意多的参数,但是都是以值传递的方式传参的。如果需要引用传参,传递的参数必须使用 [std::ref 或者std::cref](http://en.cppreference.com/w/cpp/utility/functional/ref) 进行转换。eg: 49 | 50 | void func(int& a) 51 | { 52 | a++; 53 | } 54 | 55 | int main() 56 | { 57 | int a = 42; 58 | std::thread t(func, std::ref(a)); 59 | t.join(); 60 | 61 | std::cout << a << std::endl; 62 | 63 | return 0; 64 | } 65 | 66 | 参数输出为 43, 如果没有 `std::ref` 的转换,输出的值应为 42。 67 | 68 | 除 `join` 之外,线程类也提供了其它的方法: 69 | 70 | + [swap](http://en.cppreference.com/w/cpp/thread/thread/swap) : 把两个线程的相关操作(underlying handles)互换。 71 | + [detach](http://en.cppreference.com/w/cpp/thread/thread/detach) : 允许线程对象继续独立的运行。Detach 的线程不再可连接(你不用等它们了)。 72 | 73 | int main() 74 | { 75 | std::thread t(funct); 76 | t.detach(); 77 | 78 | return 0; 79 | } 80 | 81 | 值得注意的是,如果线程函数抛出一个异常,用普通的 `try-catch` 块是捕捉不到异常的。换句话说,下面这样是不行的: 82 | 83 | try 84 | { 85 | std::thread t1(func); 86 | std::thread t2(func); 87 | 88 | t1.join(); 89 | t2.join(); 90 | } 91 | catch(const std::exception& ex) 92 | { 93 | std::cout << ex.what() << std::endl; 94 | } 95 | 96 | 放大异常可以在线程内部捕捉,然后把它存到之后可以访问到的地方。 97 | 98 | std::mutex g_mutex; 99 | std::vector g_exceptions; 100 | 101 | void throw_function() 102 | { 103 | throw std::exception("Something wrong happened"); 104 | } 105 | 106 | void func() 107 | { 108 | try 109 | { 110 | throw_function(); 111 | } 112 | catch(...) 113 | { 114 | std::lock_guard lock(g_mutex); 115 | g_exceptions.push_back(std::current_exception()); 116 | } 117 | } 118 | 119 | int main() 120 | { 121 | g_exceptions.clear(); 122 | 123 | std::thread t(func); 124 | t.join(); 125 | 126 | for(auto & e : g_exceptions) 127 | { 128 | try 129 | { 130 | if (e != nullptr) 131 | { 132 | std::rethrow_exception(e); 133 | } 134 | } 135 | catch(const std::exception & e) 136 | { 137 | std::cout << e.what() << std::endl; 138 | } 139 | } 140 | 141 | return 0; 142 | } 143 | 144 | 关于捕捉和放大异常更多资料可以阅读这里:[Handling C++ exceptions thrown from worker thread in the main thread](http://binglongx.wordpress.com/2010/01/03/handling-c-exceptions-thrown-from-worker-thread-in-the-main-thread/) 和 [How can I propagate exceptions between threads?](http://stackoverflow.com/questions/233127/how-can-i-propagate-exceptions-between-threads) 145 | 146 | 另外,`` 头文件在 `std::this_thread` 提供了很多有用的函数: 147 | 148 | + [get_id](http://en.cppreference.com/w/cpp/thread/get_id): 返回当前线程的 ID; 149 | + [yield](http://en.cppreference.com/w/cpp/thread/yield): 告诉调度程序,运行其他线程(在你处于忙等待的时候非常有用); 150 | + [sleep_for](http://en.cppfreference.com/w/cpp/thread/sleep_for): 阻塞当前线程直到指定的时段(sleep_duration); 151 | + [sleep_util](http://en.cppreference.com/w/cpp/thread/sleep_until): 阻塞当前线程直到指定的时间(sleep_time); 152 | 153 | ## 锁(Locks) ## 154 | 155 | 在最后一个例子中,访问 `g_exceptions` 向量我需要进行同步来确保在同一时间只有一个线程在进行 push 操作。因此,我使用了 mutex 。C++11 在 `` 头文件中提供了四种 mutex 来做同步操作。 156 | 157 | + [mutex](http://en.cppreference.com/w/cpp/thread/mutex): 提供了核心函数 [lock()](http://en.cppreference.com/w/cpp/thread/mutex/lock), [unlock()](http://en.cppreference.com/w/cpp/thread/mutex/unlock) 和 非阻塞的 [try_lock()](http://en.cppreference.com/w/cpp/thread/mutex/try_lock) 函数(判断 mutex 是否可用); 158 | + [recursive_mutex](http://en.cppreference.com/w/cpp/thread/recursive_mutex): 允许相同线程多次获得 mutex; 159 | + [timed_mutex](http://en.cppreference.com/w/cpp/thread/timed_mutex): 和 mutex 类相似,但是它有自己的两个核心方法 [try_lock_for()](http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for) 和 [ry_lock_until() ](http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until) 用来尝试在指定的时间段或者时间点获取 mutex ; 160 | + [recursive_timed_mutex](http://en.cppreference.com/w/cpp/thread/recursive_timed_mutex): timed\_mutex 和 recursive\_mutex 的综合体。 161 | 162 | 下面是使用 `std::mutex` 的例子(注意 `get_id()` 和 `sleep_id()` 的用法): 163 | 164 | #include 165 | #include 166 | #include 167 | #include 168 | 169 | std::mutex g_lock; 170 | 171 | void func() 172 | { 173 | g_lock.lock(); 174 | 175 | std::cout << "entered thread " << std::this_thread::get_id() << std::endl; 176 | std::this_thread::sleep_for(std::chrono::seconds(rand() % 10)); 177 | std::cout << "leaving thread " << std::this_thread::get_id() << std::endl; 178 | 179 | g_lock.unlock(); 180 | } 181 | 182 | int main() 183 | { 184 | srand((unsigned int)time(0)); 185 | 186 | std::thread t1(func); 187 | std::thread t2(func); 188 | std::thread t3(func); 189 | 190 | t1.join(); 191 | t2.join(); 192 | t3.join(); 193 | 194 | return 0; 195 | } 196 | 197 | 输出可能是这样: 198 | 199 | entered thread 10144 200 | leaving thread 10144 201 | entered thread 4188 202 | leaving thread 4188 203 | entered thread 3424 204 | leaving thread 3424 205 | 206 | 207 | `lock()` 和 `unlock` 方法简单明了,第一次锁住 mutex, 如果 mutex 不可用的话进行阻塞操作,之后对 mutex 进行解锁。 208 | 209 | 下面的例子展示了单个的线程安全容器(内部实际用的是 `std::vector`)。这个容器有类似于 `add()` 的操作,添加单个元素和 `addrange` 添加多个多个元素(其实是多次调用 `add()` )。 210 | 211 | 标注:下面的例子其实并不是真正线程安全的,有几个原因包括 va_args 的使用。并且,`dump()` 方法不应该属于 container。例子的目的仅仅在于讲解关于 mutex 的概念,而不是一个完整的,无错的,线程安全的容器。 212 | 213 | template 214 | class container 215 | { 216 | std::mutex _lock; 217 | std::vector _elements; 218 | public: 219 | void add(T element) 220 | { 221 | _lock.lock(); 222 | _elements.push_back(element); 223 | _lock.unlock(); 224 | } 225 | 226 | void addrange(int num, ...) 227 | { 228 | va_list arguments; 229 | 230 | va_start(arguments, num); 231 | 232 | for (int i = 0; i < num; i++) 233 | { 234 | _lock.lock(); 235 | add(va_arg(arguments, T)); 236 | _lock.unlock(); 237 | } 238 | 239 | va_end(arguments); 240 | } 241 | 242 | void dump() 243 | { 244 | _lock.lock(); 245 | for(auto e : _elements) 246 | std::cout << e << std::endl; 247 | _lock.unlock(); 248 | } 249 | }; 250 | 251 | void func(container& cont) 252 | { 253 | cont.addrange(3, rand(), rand(), rand()); 254 | } 255 | 256 | int main() 257 | { 258 | srand((unsigned int)time(0)); 259 | 260 | container cont; 261 | 262 | std::thread t1(func, std::ref(cont)); 263 | std::thread t2(func, std::ref(cont)); 264 | std::thread t3(func, std::ref(cont)); 265 | 266 | t1.join(); 267 | t2.join(); 268 | t3.join(); 269 | 270 | cont.dump(); 271 | 272 | return 0; 273 | } 274 | 275 | 当你运行上面程序的时候,会发现会进入死锁。原因是容器在释放 mutex 之前请求了多次。这时候你就需要使用 `std::recusive_mutex` 了,它允许线程请求同一个 mutex 多次。可以请求的最大次数没有指定,但是假如到达了请求上限,调用 lock 会抛出一个 `std::system_error` 的异常。修改上面的代码比较简单,只需要使用 `std::recursive_mutex` 代替 `std::mutex`。 276 | 277 | template 278 | class container 279 | { 280 | std::recursive_mutex _lock; 281 | // ... 282 | }; 283 | 284 | 输出类似于: 285 | 286 | 6334 287 | 18467 288 | 41 289 | 6334 290 | 18467 291 | 41 292 | 6334 293 | 18467 294 | 41 295 | 296 | 机智的你可能注意到了每一次 `func` 每一次调用都生成了相同的数字序列。这是因为种子是局部线程的,调用 `srand()` 只能从主线程上初始化种子。其他的工作线程没有被初始化,所以你每次得到的种子都是一样的。 297 | 298 | 显式的锁或者解锁可能会导致一些问题,比如忘了解锁或者和请求顺序不同的解锁可能会导致死锁。标准提供了几个类和函数帮助你解决这个问题。包装类(wrapper classes)允许使用 RAII 风格(在代码块中自动加锁和解锁)把 mutexs 一致化。这些包装器有: 299 | 300 | + [lock_guard](http://en.cppreference.com/w/cpp/thread/lock_guard) : 当对象构造它的时候尝试去请求自己的 mutex (调用 lock),当对象析构它的时候会自动释放 mutex (调用 unlock()),这是一个不能拷贝的类; 301 | + [unique_lock](http://en.cppreference.com/w/cpp/thread/unique_lock) : 和 lock\_guard不同,它是一个通用的 mutex 包装器。提供了延迟锁(defferd locking), 时间锁(time locking), 递归锁(recursive locking), 转移所有权而用条件变量。这个类也是不可拷贝的类,但是他支持 move 操作。 302 | 303 | 使用这些包装器重写上面的容器类之后是这样的: 304 | 305 | template 306 | class container 307 | { 308 | std::recursive_mutex _lock; 309 | std::vector _elements; 310 | public: 311 | void add(T element) 312 | { 313 | std::lock_guard locker(_lock); 314 | _elements.push_back(element); 315 | } 316 | 317 | void addrange(int num, ...) 318 | { 319 | va_list arguments; 320 | 321 | va_start(arguments, num); 322 | 323 | for (int i = 0; i < num; i++) 324 | { 325 | std::lock_guard locker(_lock); 326 | add(va_arg(arguments, T)); 327 | } 328 | 329 | va_end(arguments); 330 | } 331 | 332 | void dump() 333 | { 334 | std::lock_guard locker(_lock); 335 | for(auto e : _elements) 336 | std::cout << e << std::endl; 337 | } 338 | }; 339 | 340 | 341 | 虽然 `dump()` 方法应该声明为 const , 因为它并没有修改容器的状态。但是如果你声明为 const 以后,编译器会报如下错误: 342 | 343 | ‘std::lock_guard<_Mutex>::lock_guard(_Mutex &)' : cannot convert parameter 1 from ‘const std::recursive_mutex' to ‘std::recursive_mutex &' 344 | 345 | 一个 mutex(regardless which implement is used) 必须被请求和释放,实现上调用了非常量的函数 `lock()` 和 `unlock()` 。因此 `lock_guard` 的参数逻辑上不应该是常量。解决这个问题的方法是使用 `mutable` 声明 mutex 。Mutable 允许在常量方法中使用。 346 | 347 | template 348 | class container 349 | { 350 | mutable std::recursive_mutex _lock; 351 | std::vector _elements; 352 | public: 353 | void dump() const 354 | { 355 | std::lock_guard locker(_lock); 356 | for(auto e : _elements) 357 | std::cout << e << std::endl; 358 | } 359 | }; 360 | 361 | 这些 wrapper guards 的构造函数已经负载(overloads)参数指示锁的策略。可用的策略有: 362 | 363 | + `defer_lock_t` 类型的 `defer_lock` : 不请求 mutex ; 364 | + `try_to_lock_t` 类型的 `try_to_lock` : 试图请求 mutex ,不阻塞 ; 365 | + `adopt_lock_t` 类型的 `adopt_lock` : 用 mutex 唤醒调用线程 ; 366 | 367 | 这些策略的声明像这样: 368 | 369 | struct defer_lock_t { }; 370 | struct try_to_lock_t { }; 371 | struct adopt_lock_t { }; 372 | 373 | constexpr std::defer_lock_t defer_lock = std::defer_lock_t(); 374 | constexpr std::try_to_lock_t try_to_lock = std::try_to_lock_t(); 375 | constexpr std::adopt_lock_t adopt_lock = std::adopt_lock_t(); 376 | 377 | 除了这些 mutex 的包装器之外,标准也提供了几个对一个或者多个 mutex 加锁的方法: 378 | 379 | + [lock](http://en.cppreference.com/w/cpp/thread/lock): 使用避免死锁算法对 mutexes 加锁(通过调用 `locks()`, `try_locks` 和 `unlock()` )。 380 | + [try_lock](http://en.cppreference.com/w/cpp/thread/try_lock): 按照指定的 mutexes 顺序调用 `try_lock()` 尝试调用 mutex 。 381 | 382 | 下面是一个死锁的例子:我们有一个元素容器并且有一个从一个容器和另外一个容器交换的方法 `exchange`。 为了达到线程安全,在两个容器中同步存取,请求不同容器的 mutex 。 383 | 384 | template 385 | class container 386 | { 387 | public: 388 | std::mutex _lock; 389 | std::set _elements; 390 | 391 | void add(T element) 392 | { 393 | _elements.insert(element); 394 | } 395 | 396 | void remove(T element) 397 | { 398 | _elements.erase(element); 399 | } 400 | }; 401 | 402 | void exchange(container& cont1, container& cont2, int value) 403 | { 404 | cont1._lock.lock(); 405 | std::this_thread::sleep_for(std::chrono::seconds(1)); // <-- forces context switch to simulate the deadlock 406 | cont2._lock.lock(); 407 | 408 | cont1.remove(value); 409 | cont2.add(value); 410 | 411 | cont1._lock.unlock(); 412 | cont2._lock.unlock(); 413 | } 414 | 415 | 假定这个函数被不同的线程访问,从 容器1 中移除一个元素添加到 容器2 中;然后把移除 容器2 中的元素添加到 容器1 中。这样会导致死锁(如果线程上下文仅仅在第一次请求的时候从一个线程到另外一个线程切换)。 416 | 417 | int main() 418 | { 419 | srand((unsigned int)time(NULL)); 420 | 421 | container cont1; 422 | cont1.add(1); 423 | cont1.add(2); 424 | cont1.add(3); 425 | 426 | container cont2; 427 | cont2.add(4); 428 | cont2.add(5); 429 | cont2.add(6); 430 | 431 | std::thread t1(exchange, std::ref(cont1), std::ref(cont2), 3); 432 | std::thread t2(exchange, std::ref(cont2), std::ref(cont1), 6); 433 | 434 | t1.join(); 435 | t2.join(); 436 | 437 | return 0; 438 | } 439 | 440 | 为了修正这个问题,你可以使用 `std::lock` 保证在 deadlock-free 的方式下请求 mutex : 441 | 442 | void exchange(container& cont1, container& cont2, int value) 443 | { 444 | std::lock(cont1._lock, cont2._lock); 445 | 446 | cont1.remove(value); 447 | cont2.add(value); 448 | 449 | cont1._lock.unlock(); 450 | cont2._lock.unlock(); 451 | } 452 | 453 | ## 条件变量(Condition variables) ## 454 | 455 | 456 | ---- 457 | 458 | ## 扩展资料 ## 459 | 460 | + [C++11 threads, locks and condition variables](http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables) 461 | + [mutable Data Members (C++)](http://msdn.microsoft.com/en-us/library/4h2h0ktk.aspx) 462 | + [C++'s mutable and conceptual constness](http://www.highprogrammer.com/alan/rants/mutable.html) 463 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Type-Inference.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "类型自动推导" 4 | category : "Language" 5 | --- 6 | 7 | 8 | 在 C++03 中(包括C),使用一个变量比如显式的指定它的类型。然而,随着模板类型和模板元编程技术的的引入,对象的类型,详细的定义一个函数的返回值,可能不易表达。因此,临时变量的存储变的很难,可能需要去了解具体模板元库的实现细节。 9 | 10 | C++11用两种方式来解决这种问题。第一种是使用 auto 关键字来定义直接初始化的变量类型(也就是所谓的类型推导,根据初始化的值来推导类型)。 11 | 12 | auto some_strange_callable_type = std::bind(&some_function, _2, _1, some_object); 13 | auto other_variable = 5; 14 | 15 | `some_strange_callable_type` 类型比较简单,不用管 `std::bind` 的实现细节(参数、返回值,是否重载),它的类型对于编译器来说很容易得到(编译器语义分析的一部分),而用户判定它的类型不是那么容易。 16 | 17 | `other_variable` 是一个 `int`, 和整型字面量类似。 18 | 19 | 此外,`decltype`可以在编译的时候判断出一个表达式的类型(第二种方式),例如: 20 | 21 | int some_int; 22 | decltype(some_int) other_integer_variable = 5; 23 | 24 | 和 `auto` 配合使用非常牛逼,auto 变量的具体类型只有编译器知道,而 decltype 对于大量的运算符重载和类型特化的表示非常用用。 25 | 26 | 使用 auto 可以减少很多代码冗余,比如说,以前我们是这样写的: 27 | 28 | for (std::vector::const_iterator itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 29 | 30 | 现在可以这样写: 31 | 32 | for (auto itr = myvec.cbegin(); itr != myvec.cend(); ++itr) 33 | 34 | 代码更短了(而且假如类型很长的话,我们不用再费劲的去记忆类型的名字,我觉得这个用法太叼了)。尤其是在模板类型嵌套的时候,比如: 35 | 36 | std::map >::const_iterator 37 | 38 | 当然,这种情况下使用 `typedef` 也是一种好方法。 39 | 40 | `decltype`和`auto`可能推导出来的类型不同: 41 | 42 | #include 43 | int main() { 44 | const std::vector v(1); 45 | auto a = v[0]; // a has type int 46 | decltype(v[1]) b = 1; // b has type const int&, the return type of 47 | // std::vector::operator[](size_type) const 48 | auto c = 0; // c has type int 49 | auto d = c; // d has type int 50 | decltype(c) e; // e has type int, the type of the entity named by c 51 | decltype((c)) f = c; // f has type int&, because (c) is an lvalue 52 | decltype(0) g; // g has type int, because 0 is an rvalue 53 | } 54 | 55 | 个人感觉,站在这个角度来说,decltype 比 auto 功能更强大一些; 反过来,auto 比 decltype 更精巧。 56 | 57 | `decltype((variable))`的结果永远是引用,而`decltype(variable)`结果只有当 variable 本身就是一个引用时才是引用。 58 | 59 | ## auto 与 函数 ## 60 | auto与普通函数,模板函数,lambda函数结合使用,你会瞬间感觉c++变得优雅了很多。 61 | 62 | #include 63 | #include 64 | using namespace std; 65 | 66 | template 67 | void myprint(T& var) 68 | { 69 | cout<; 79 | 80 | auto printfunc = normalfunc; 81 | for_each(data,data+datalen,printfunc); 82 | 83 | return 0; 84 | } 85 | 86 | ## 参考资料 ## 87 | 88 | + [Type inference](https://en.wikipedia.org/wiki/C%2B%2B11#Type_inference) 89 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Uniform-Initialization.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "统一的初始化方式" 4 | category : "Language" 5 | --- 6 | 7 | 1. 本文来自:[C++11 FAQ中文版:统一初始化的语法和语义](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-unified-initialization-syntax-and-semantics.html) 8 | 2. 暂无测试代码 9 | 10 | C++03 的类型初始化有多种方式,因此也引来很多问题,如果错用的时候,错误的调试信息会非常模糊。 11 | 12 | 比如以下初始化变量的方式: 13 | 14 | std::string a[] = {"Hello", "world"}; // 正确:初始化数组变量 15 | // 错误:初始化列表引用在非聚合的向量上 16 | std::vector v = {"hello", "world"}; 17 | void f(string a[]); 18 | f ({"foo", "bar"}); // 语法错误,把一个块(block)作为了参数 19 | int a = 2; // "赋值风格"的变化 20 | int aa[] = {2, 3}; // 用初始化列表进行的赋值风格的初始化 21 | complex z(1, 2); // 函数风格的初始化 22 | x = Ptr(y); // "函数风格" 转换/赋值/构造操作 23 | 24 | 25 | 所谓统一的初始化方式,就是把之前 C++ 风格的各种初始化对象方式,统一为一种方式(语法): 26 | 27 | X x1 = X{1, 2}; 28 | X x2 = {1, 2}; 29 | X x3{1, 2}; 30 | X * p = new X{1, 2}; 31 | 32 | struct D : X { 33 | D(int x, int y) : X{x, y} {/* ... */} 34 | } 35 | 36 | struct S { 37 | int a[3]; 38 | S(int x, int y, int z) : a{x, y, z} {/* ... */} 39 | } 40 | 41 | 重点在于: X{a}在所有的可执行代码中都创建了同一个相同的值,所以使用"{}"进行的初始化,在合法的使用的时候,在任何地方都产生相同的结果。例如: 42 | 43 | X x{a}; 44 | X* p = new X{a}; 45 | z = X{a}; 46 | f({a}); 47 | return {a}; 48 | 49 | 50 | ## 扩展资料 ## 51 | 52 | + [C++11 FAQ中文版:统一初始化的语法和语义](http://www.chenlq.net/books/cpp11-faq/c-0-x-faq-chinese-version-unified-initialization-syntax-and-semantics.html) 53 | 54 | -------------------------------------------------------------------------------- /tools/_posts/2013-07-13-Unrestricted-Unions.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "无限制的 Unions" 4 | category : "Language" 5 | --- 6 | 7 | 译自:[Unrestricted unions](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 8 | 9 | 在 C++03 中 union 的成员不能是一个对象类型。例如, unions 不能包含任何定义了一个 non-trivial 的构造函数。C++11 解除了一些限制: 10 | 11 | + Unions 可以包含有 non-trivial 对象的成员; 12 | + 如果这样的话,编译器不再提供隐式的构造函数,需要自己去定义. 13 | 14 | 下面是一个简单的 C++11 unions 定义: 15 | 16 | #include // Required for placement 'new' 17 | 18 | struct Point { 19 | Point() {} 20 | Point(int x, int y) : x_(x), y_(y) {} 21 | int x_, y_; 22 | }; 23 | 24 | union U { 25 | int z; 26 | double w; 27 | Point p; // Illegal in C++03; legal in C++11 28 | U() { 29 | new (&p) Point(); // Due to the Point member, a constructor definition is now required. 30 | } 31 | }; 32 | 33 | 34 | ## 扩展资料 ## 35 | 36 | + [Unrestricted unions](https://en.wikipedia.org/wiki/C%2B%2B11#Right_angle_bracket) 37 | + [What is a non-trivial constructor in C++?](http://stackoverflow.com/questions/3899223/what-is-a-non-trivial-constructor-in-c) 38 | -------------------------------------------------------------------------------- /tools/_posts/2013-11-04-constexpr-and-const-expression.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "constexpr和常量表达式" 4 | category : "Language" 5 | --- 6 | 7 | 常量表达式(const expression) 是指值不会改变并且在编译过程就能得到计算结果的表达式。 8 | 9 | ## constexpr 变量 ## 10 | 11 | C++11 新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式。声明为constexpr 的变量一定是一个常量,而且必须用常量表达式初始化。 12 | 13 | constexpr int mf = 20; // 20 是常量表达式 14 | constexpr int limit = mf + 1; // mf + 1 是常量表达式 15 | constexpr sz = size(); // 只有当size是一个constexpr函数时 16 | // 才是一条正确的语句 17 | 18 | ## constexpr 与 数组 ## 19 | c++11 允许声明大小为constexpr的数组。 20 | 21 | constexpr sz = size(); 22 | int data[sz] = {1,2,3}; //if sz == 3 23 | 24 | ## 指针和 constexpr ## 25 | 26 | 在 constexpr 声明中如果定义了一个指针,constexpr 仅对指针有效,与指针所指的对象无关。 27 | 28 | const int *p = nullptr; // p 是一个指向整数常量的指针 29 | constexpr int *q = nullptr; // q 是一个指向整数的常量指针 30 | 31 | p 是可变的,p指向的对象是不可变的。q是不可变的,q指向的对象是可变的。 32 | 33 | ## 参考资料 ## 34 | 35 | + C++ Primer(第5版)中文版 P59 36 | -------------------------------------------------------------------------------- /tools/_posts/2013-11-04-type-alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "类型别名" 4 | category : "Language" 5 | --- 6 | 7 | 有两种方法可用于定义类型别名,第一种方法是使用`typedef`关键字,这个C++03就已经支持了。第二种方式是使用C++11新的声明方式`using`关键字。方法很简单,比如: 8 | 9 | using MyUint = unsigned int; 10 | 11 | 那么 MyUint 和 unsigned int 等价了。 12 | 13 | 随即,你可能想到了一个问题:`typedef`和`using`的区别在哪里? 14 | 15 | 我找了一个sof的帖子:[Difference between typedef and C++11 type alias [duplicate]](http://stackoverflow.com/questions/18287151/difference-between-typedef-and-c11-type-alia) 16 | 17 | > There is absolutely no difference between both. 18 | > 19 | > If you take a look at the standard : 20 | > 21 | > 7.1.3 The typedef specifier [dcl.typedef ] 22 | > 23 | > A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id. 24 | > 25 | > 7.3.3 The using declaration [namespace.udecl] 26 | > 27 | > If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2), the name introduced by the using-declaration is treated as a typedef-name. 28 | > 29 | > However from this page : http://en.cppreference.com/w/cpp/language/type_alias 30 | > 31 | > It is said : 32 | > 33 | > Type aliases are similar to typedefs, however, have the advantage of working with templates. 34 | > 35 | > It is seems that this : 36 | > 37 | > // template type alias 38 | > template using ptr = T*; 39 | > // the name 'ptr' is now an alias for pointer to T 40 | > ptr x; 41 | > Is only possible with the using directive. 42 | > 43 | > And do not forget that this is a C++11 feature. Some compilers do not support it yet. 44 | 45 | 我想,上面的这段回复足够回答你的问题了。 46 | -------------------------------------------------------------------------------- /tools/_posts/2013-12-15-compiler-support.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "主流编译器支持情况" 4 | category : "Compiler" 5 | --- 6 | 7 | 主流编译对C++11的支持情况,欢迎补充: 8 | 9 | + GCC: [C++0x/C++11 Support in GCC](http://gcc.gnu.org/projects/cxx0x.html) 10 | + VS2010: [C++0x Core Language Features In VC10: The Table](http://blogs.msdn.com/b/vcblog/archive/2010/04/06/c-0x-core-language-features-in-vc10-the-table.aspx) 11 | + VS2012: [C++11 Features (Modern C++)](http://msdn.microsoft.com/en-us/library/hh567368\(v=vs.110\).aspx) 12 | + VS2013: [Support For C++11 Features (Modern C++)](http://msdn.microsoft.com/en-us/library/hh567368\(v=vs.120\).aspx) 13 | + Clang: [C++14, C++11 and C++98 Support in Clang](http://clang.llvm.org/cxx_status.html) -------------------------------------------------------------------------------- /tools/_posts/2013-12-15-cpp-reading-list.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "[译]C++阅读列表" 4 | category : "Book Recommend" 5 | --- 6 | 7 | 原文链接: [C++ Reading List](http://www.drdobbs.com/cpp/c-reading-list/240155654?pgno=1) 8 | 9 | 10 | ## 1. [C++ Programming Language, 4th Edition](http://www.amazon.com/dp/0321563840) ## 11 | 12 | ![C++ Programming Language](http://image.cpp1x.org/CPP_Programming_Languages_01_full.jpg) 13 | 14 | 作者: [Bjarne Stroustrup](http://www.stroustrup.com/) 15 | 16 | 这本书堪称C++语言的“圣经”,表达了语言本身以及它的特性,由 C++ 之父 Stroustrup 完成。很多读者可能把 ANSI C++ 文档作为更权威的信息来源,但是它只是对于已经了解这门语言的人一个简洁的参考资源。这本书恰好相反,友好的(详尽的)解释了新特性,以及一些使用建议和应该避免的做法,让读者让容易理解一些特性。就此而言,这本书可以作为一本参考教程。 17 | 18 | 这本书没有使用等宽字体,垂直不对齐影响了美观。尽管如此,代码排版比以前的版本好多了。第二个问题是这本书没有完全的涵盖 C++ 语言,第 4 版有 1328 页,原版大约 1000 页。Stroustrup 多介绍了一些库,这些数字表明 C++ 变得更加的复杂。These concerns notwithstanding, I don't see how serious C++ programmers looking to use the new features of the language can proceed without this work. 19 | 20 | 极力推荐这本书! 21 | 22 | ## 2. [C++ Primer, 5th Edition](http://www.amazon.com/Primer-5th-Edition-Stanley-Lippman/product-reviews/0321714113) ## 23 | 24 | ![C++ Primer, 5th Edition](http://image.cpp1x.org/CPP_Primer_02_full.jpg) 25 | 26 | 作者: tanley Lippman, Josée Lajoie, Barbara Moo 27 | 28 | 多年以来,C++ Primer 都被作为 C++ 指定教程来,第五版使用 C++11 全部重写,900 页密集的页面描述了 C++ 语言的方方面面。这还是保守说法,它的内容已经远远的超过 primer 这个词可以表达的范畴。没有一节告诉你可以“快速的学习这门语言”,而且坚持不懈的用文字覆盖了语言的每一个方面和 C++ 程序员可能会遇到的问题。有没有怀疑过假如我们让析构函数作为 C++11 中提到的"deleted function"会怎么样呢? 我想过!这本书详细的回答了这个问题。 29 | 30 | 这本书只覆盖语言本身,通过讨论库和在附录中介绍了库中的头文件和算法的梗概(如果想详细的了解C++库的话,我建议看 the C++ Standard Library: A Tutorial and Reference,也就是我们下一个讨论的话题)。对比这两本书,Stroustrup 的 C++ Programming Language, 4th Ed. 是这本书的竞争对手,Stroustrup 的书主要是一个参考手册,反之,这本书更偏向应用。讨论内容形如如果你错误的使用特性会导致那些问题,包括了更多的代码,相比而言,比 Stroustrup 的书有更多的指导性。 31 | 32 | 我怀疑这本书早期是为了有语言基础的人写的。我不建议把这本书作为入门级书籍(学生或者第一次接触这门语言的人)。但是对于 C++ 开发人员,想去使用 C++11 新特性的人来说的话,这本书是绝佳选择。 33 | 34 | ## 3. [The C++ Standard Library, 2nd Edition](http://www.amazon.com/dp/0321623215) ## 35 | 36 | ![The C++ Standard Library, 2nd Edition](http://image.cpp1x.org/CPP_StandardLibrary_03_full.jpg) 37 | 38 | 作者: Nicolai Josuttis 39 | 40 | 这是 C++ 标准库经典的教程和参考手册第二版,专门为了C++11而作。不像许多书籍一样覆盖所有的库和API,它不是以解释如何进行函数调用而出名的。它把标准库(大部分STL)划分成子块,然后解释基本组件的设计。只详细讲解了一部分API,最后对其他的API进行概述。章节中会解释设计思想、内部实现、应用以及对不同的选择进行对比。 41 | 42 | 那些讨论有说服力、清晰而且非常丰富。例如,在 STL 函数对象和 lambda 用 12 页仅仅解释函数对象是什么和为什么你要用它们。之后作者又解释了预定义函数对象(用另外的12页)。这些都为后面的讨论奠定了基础: lambda 之旅。因此,它的所有讲解超过了1000页。有些解释也非常的精简,完整的代码强调关键的部分,你可以看到精确的看到一个函数怎样使用或者如何实现的。 43 | 44 | 这本书的第一版被很多C++程序员当成经典,第二版作为第一版的升级版,必定延续经典,推荐给你们。 45 | 46 | 47 | ## 4. [C++ Concurrency in Action](http://www.amazon.com/dp/1933988770) ## 48 | 49 | ![C++ Concurrency in Action](http://image.cpp1x.org/CPP_ConcurrencyInAction_04_full.jpg) 50 | 51 | 作者: Anthony Williams 52 | 53 | This book is a deep dive into concurrency using C++11 features. It's written by the primary developer and maintainer of the Boost Thread library, which is the basis for much of the language's new threading support. In sum, it's written by a highly qualified author. 54 | 55 | It starts off with the basics, assuming the reader has good (even very good) command of the language, but is new to writing parallel code. It steps through the problems posed by concurrency, the solutions and limitations of mutual exclusion, and how they're implemented in C++11. It then moves through the C++ memory model and atomic types. Finally, it digs into the design of lock-based and lock-free data structures. It's the best treatment I've seen of these topics since Herb Sutter discussed them in our pages. 56 | 57 | The book thoughtfully covers topics of real importance that are frequently overlooked in other treatments; namely, designing code for multithreading and debugging threaded applications. Of these, both treatments are too short in my view. A large part of the book is reference material (almost 130 pages on the C++ threading library alone). And another appendix is a full message-framework all laid out in code with explanations throughout. There's no doubt the author has done his homework. 58 | 59 | I have a few small complaints, but they are truly small. The first is that you must have in-depth knowledge of C++, good-enough-to-skate-by won't be enough for this book. Second, the author consigns thread pools to the final chapters under the rubric of "Advanced Threading." In my opinion, pools mark the point at which concurrency gets really interesting; but here, it's where the author stops. Predictably, given this orientation, he does not ever address the Actor model, which gets a single passing mention in the index. It's as if this approach did not exist at all, even though it's integral to several languages — just not, in Williams' view, C++. 60 | 61 | Overall, these complaints reflect my preferences, rather than any flaws that invalidate the book. Williams' work is an excellent treatment and likely to be the defining title in this area for a long time to come. 62 | 63 | If you want a closer look at content from this book, we recently published an excerpt from it, in a popular article: "Waiting for One-Off Events with Futures." 64 | 65 | ## 5.[Secure Coding in C and C++, 2nd Edition](http://www.amazon.com/Secure-Coding-2nd-Software-Engineering/dp/0321822137)## 66 | 67 | ![Secure Coding in C and C++, 2nd Edition](http://image.cpp1x.org/Secure_Code_CPP_05_full.jpg) 68 | 69 | 作者: Robert Seacord 70 | 71 | This is the definitive work on writing secure code in C/C++. This new edition, which nearly doubles the size of the original 2005 work, illustrates how much we've learned in the interim about attacks on code. In fact, in reading through this book, one feels almost overwhelmed by the variety and imagination of attacks today. However, as the author capably explains, security is more than just the implementation of counter-techniques, but weaves security throughout the implementation. Only through such a consistent mindset, he argues, can the damage wrought by future attack methods be contained. 72 | 73 | The book details numerous kinds of hacks and what can be done to prevent them or make them so difficult as to discourage the hacker. The explanations are remarkably well written and the code is clear. However, it requires a more-advanced formation than most books on programming: It needs the reader to have a fairly good idea of how C and C++ programs execute and what is happening at the machine level. Because the required information is not provided in the text, it's not possible to get the true value from the suggestions without having done this homework first. 74 | 75 | 76 | For developers who can follow along and understand the inner workings of program execution, this book is not only an excellent guide, but a revelation. For example, Seacord's discussions of how attacks are enabled by doubly freeing an allocated chunk of memory highlights a feature of this simple coding error that might be completely overlooked if you were not a security expert. The explanation is illuminating. 77 | 78 | The hands-on nature of this volume is exemplified by frequent recommendations of tools to use to verify code and lock down access mechanisms that crackers like to exploit. This is an excellent volume that will very definitely make you a more aware, and certainly better, programmer. 79 | 80 | ## 6. [Real-Time C++](http://www.amazon.com/dp/3642346871) ## 81 | 82 | ![Real-Time C++](http://image.cpp1x.org/Real_Time_CPP_06_full.jpg) 83 | 84 | 作者: Chris Kormanyos 85 | 86 | This is a gentle introduction to using C++11 in real-time projects. The author presents several basic projects and shows how he used C++11 to code them. He starts with a product that targets an Atmel AVR microcontroller, which he programs using the GCC toolchain, explaining along the way: the design, implementation in hardware and code, and finally how to flash and execute the program. It turns on LEDs under various circumstances. He then moves on to more-ambitious projects, such as writing low-level hardware drivers in C++. In the final chapters, he examines extending the standard C++ library and the STL for embedded purposes. 87 | 88 | The book is approachable and the code fairly clean. It shows that C++11 is a reasonable choice for embedded work. Overall, this is a good tutorial for C++ developers who want to get their feet wet in embedded programming, but due to the choice of sample projects, it probably won't appeal to programmers already active in that line of work. 89 | 90 | 笔者注: 91 | 92 | 只翻译了前三本书的简介,后面的没有翻译。笔者对并发、安全、Real-Time了解不多,不敢随便翻译(了解相关技术的同学,可以帮忙翻译)。说一下自己的看法: 93 | 94 | > C++ Programming Language 和 C++ Primer 这两本书选一本看就行了,对于入门,我推荐前者。有C++基础,我推荐后者。我和这篇文章作者的观点相同,C++ Primer 真的不适合作为入门书籍,门槛高,让人望而却步。 95 | > 96 | > The C++ Standard Library 这本书看过人的都知道,是非常好的一本工具书籍,目前第二版没有中文版。 97 | > 后面这三本书不适合初学者,看了简介以后,个人最想看 C++ Concurrency in Action。 98 | > 99 | > C++11 增加了很多新的特性和标准库,但是不用看太多的书籍。仅 C++ Primer, 5th Edition 和 The C++ Standard Library, 2nd Edition 这两本书足够。 100 | > 101 | > C++ 的最大问题在于它的繁杂,给程序员很多奇淫的机会,其实这本身对学 C++ 的人没有太多的好处。语言毕竟最终都是要面向应用的,执迷于技巧容易本末倒置,忽略了真正关注的东西。把 C++ 当成工具对待(从这个角度看C++11,它的确比以前强大了太多)。 102 | 103 | _这篇文章原本是答应 [博乐在线](http://blog.jobbole.com/) 要翻译的,结果因为当时没时间就没有翻译。昨天翻译才发现自己确实没有能力全部翻译,就成了现在的半成品,也就不再投稿给 博乐在线 了,抱歉。_ -------------------------------------------------------------------------------- /tools/_posts/2013-12-29-smart-pointer.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "智能指针" 4 | category : "STL" 5 | --- 6 | 7 | C++98只支持一个智能指针[auto_ptr](http://www.cplusplus.com/reference/memory/auto_ptr/?kw=auto_ptr),然而auto_ptr实在是太简陋了(赋值导致的指针转移,一直为人诟病)。 8 | 9 | C++11废弃了`auto_ptr`,引入了两种智能指针: 10 | 11 | 1. `shared_ptr` 12 | 13 | 共享拥有对象的所有权,使用引用技术来实现。具体可看: [http://www.cplusplus.com/reference/memory/shared_ptr/](http://www.cplusplus.com/reference/memory/shared_ptr/)。 14 | 15 | 2. `unique_ptr` 16 | 17 | 唯一拥有对象的所有权,auto\_ptr的替代品,但是修复了auto\_ptr的缺陷,指针对象以赋值的方式转让。详细可看: [http://www.cplusplus.com/reference/memory/unique_ptr/](http://www.cplusplus.com/reference/memory/unique_ptr)。 18 | 19 | C++11允许为智能指针设置一个删除器,当设定一个删除器时,智能指针析构会用指定的删除器取代默认的delete操作。如果你使用指针指针管理的资源不是用new分配的,记得一定要为这个智能指针设定一个删除器。 20 | 21 | 其实C++11还存在一种"弱"智能指针,叫[weak\_ptr](http://www.cplusplus.com/reference/memory/weak_ptr)。它不能控制所指对象的生存期,指向由 shared\_ptr 管理的对象(不会改变引用计数),主要用途是解决智能指针循环引用导致的内存泄漏。 22 | 23 | 我们知道不可以为auto_ptr传入一个动态数组,否则会导致未定义的行为(new[]->delete)。在C++11中的这两个智能指针都可以传入一个动态数组,不过略有不同。 24 | 25 | + 对于`share_ptr`,传入一个动态数组,你需要同时为它指定一个删除器。如: `shared_ptr sp(new int[1024], [](int *p) {delete[] p;});` 可以看出,我们写一个lambda表达式,用 delete[] 去释放一个数组。 26 | + 对于`unique_ptr`就没这么麻烦了,你可以直接使用`[]`用来标识你要传入一个动态数组。如: `unique_ptr sp(new int[1024]);`。 27 | 28 | 29 | ## 扩展资料 ## 30 | 31 | + [C++ smart pointers wikipedia](http://en.wikipedia.org/wiki/Smart_pointer#C.2B.2B_smart_pointers) 32 | + [When is std::weak_ptr useful?](http://stackoverflow.com/questions/12030650/when-is-stdweak-ptr-useful) -------------------------------------------------------------------------------- /tools/_posts/2014-01-14-Bjarne-Stroustrup-the-essence-of-cpp.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "[Bjarne Stroustrup]C++本质" 4 | category : "Resource" 5 | --- 6 | 7 | 8 | 9 | 10 | ppt 下载地址: [http://vdisk.weibo.com/s/G-kaugh7dbcL](http://vdisk.weibo.com/s/G-kaugh7dbcL) 11 | -------------------------------------------------------------------------------- /tools/_posts/2014-02-07-cpp11-open-source-projs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "C++11开源项目汇总" 4 | category : "Resource" 5 | --- 6 | 7 | 以下排名不分先后: 8 | 9 | ## [facebook/folly](https://github.com/facebook/folly) ## 10 | 11 | > Folly (acronymed loosely after Facebook Open Source Library) is a library of C++11 components designed with practicality and efficiency in mind. It complements (as opposed to competing against) offerings such as Boost and of course std. In fact, we embark on defining our own component only when something we need is either not available, or does not meet the needed performance profile. -- [Overview](https://github.com/facebook/folly/blob/master/folly/docs/Overview.md) 12 | 13 | 14 | 欢迎补充~ 15 | 16 | -------------------------------------------------------------------------------- /tools/_posts/2014-03-05-class-inner-init.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout : post 3 | title : "类内部初始化" 4 | category : "Language" 5 | --- 6 | 7 | ## 类内部初始化 ## 8 | c++11中能像c#/java一样在成员变量声明的同时初始化,可通过赋值初始化,也可通过列表初始化,见如下代码: 9 | 10 | #include 11 | using namespace std; 12 | 13 | struct foo_data 14 | { 15 | public: 16 | foo_data() : val('b'){} 17 | 18 | char val = 'a'; 19 | char val2{'x'}; 20 | }; 21 | 22 | int main(int argc,char* argv[]) 23 | { 24 | foo_data data; 25 | cout< 11 | #include 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | /* 17 | //c/c++ type 18 | void printf(const char *s) 19 | { 20 | while (*s) 21 | { 22 | if (*s == '%' && *(++s) != '%') 23 | throw std::runtime_error("invalid format string: missing arguments"); 24 | std::cout << *s++; 25 | } 26 | }*/ 27 | 28 | //c++11 type 29 | //not implements well 30 | template 31 | void cxx11_printf(const char* s, T value, Args... args) 32 | { 33 | while (*s) 34 | { 35 | if (*s == '%' && *(++s) != '%') 36 | { 37 | std::cout << value; 38 | cxx11_printf(*s ? ++s : s, args...); 39 | return; 40 | } 41 | std::cout << *s++; 42 | } 43 | assert(false); 44 | } 45 | 46 | int main(int argc,char* argv[]) 47 | { 48 | cxx11_printf("%d %s",1,"hello world"); 49 | return 0; 50 | } 51 | 52 | 上面的代码向你展示了怎样使用可变模板。 53 | 54 | ## tuple ## 55 | 56 | 下面展示了C++11标准库中应用可变参数模板产生的新容器tuple的用法: 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | using namespace std; 64 | 65 | struct foo_data 66 | { 67 | friend ostream& operator<< (ostream& out,const foo_data& data); 68 | public: 69 | foo_data(const char _val) : val(_val){} 70 | public: 71 | char val = 'a'; 72 | }; 73 | ostream& operator<< (ostream& out,const foo_data& data) 74 | { 75 | out< tupInfo = make_tuple('x', string("C++ 11")); 83 | cout<<"tuple : "<(tupInfo)<<"\t"<(tupInfo)<> infoList = { 87 | make_tuple(0,foo_data('a'),string("this is a")), 88 | make_tuple(1,foo_data('b'),string("this is b")), 89 | make_tuple(2,foo_data('c'),string("this is c")), 90 | make_tuple(3,foo_data('d'),string("this is d")) 91 | }; 92 | cout<<"tuple list one : "<(tupleval)<<"\t"; 96 | cout<(tupleval)<<"\t"; 97 | cout<(tupleval)<<"\t"; 98 | cout<(0,foo_data('a'),string("this is a")), 105 | tuple(1,foo_data('b'),string("this is b")), 106 | tuple(2,foo_data('c'),string("this is c")), 107 | tuple(3,foo_data('d'),string("this is d")) 108 | }; 109 | cout<<"tuple list two : "<(tupleval)<<"\t"; 113 | cout<(tupleval)<<"\t"; 114 | cout<(tupleval)<<"\t"; 115 | cout< 12 | #include 13 | using namespace std; 14 | 15 | struct foo_data 16 | { 17 | public: 18 | foo_data(const char _val) : val(_val){} 19 | public: 20 | char val = 'a'; 21 | }; 22 | 23 | int main(int argc,char* argv[]) 24 | { 25 | cout<<"sizeof foo_data::val is "<