第一篇 C++11 改进我们的程序 第一章 使用C++11让程序更简洁,更现代化 一.类型推导 1.auto类型推导 auto关键字被赋予了新的意义 ,c++98及之前代表”具有自动存储器的局部变量”,且用处不大,现在用来进行类型推导,简化了代码
1 2 3 4 5 6 7 auto x = 5 ; auto pi = new auto (1 ); const auto *v = &x, u = 6 ; static auto y = 0.0 ; auto int r; auot s;
auto的推导 ,先引出一组例子
1 2 3 4 5 6 7 8 9 10 11 12 int x = 0 ;auto * a = &x; auto b = &x; auto & c = x; auto d = c; const auto e = x; auto f = e; const auto & g = x; auto & h = g;
当不声明为指针或引用时,auto推导时将抛弃cv限定符
当声明为指针或引用时,auto推导时将保持cv属性
auto的限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void func (auto a = 1 ) {} struct Foo { auto var1 = 0 ; static const auto var2 = 0 ; }; template <typename T>struct Bar {};int main () { int arr[10 ] = {0 }; auto aa = arr; auto rr[10 ] = arr; Bar<int > bar; Bar<auto > bb = bar; return 0 ; }
何时使用auto
最常用的地方应该就是迭代器类型的推导,一般容器的类型已经很长了,再加上嵌套从属名称就更长了,auto可以有效进行这种类型代码的简化,已经获得函数返回值时,也可以直接用auto定义变量
2.decltype关键字 该关键字和auto有点类似,两者一起使用更是加大了类型推导的灵活度,与auto相比,auto像是一个占位符,而decltype确实就像是类型
获知表达式的类型 ,引出一组例子
1 2 3 4 5 6 7 8 9 10 11 12 int x = 0 ;decltype (x) y = 1 ; decltype (x + y) z = 0 ; const int & i = x; decltype (i) j = y; const decltype (z) * p = &z; decltype (z) * pi = &z; decltype (pi)* pp = π
decltype的推导规则
exp是标识符,类访问表达式,推导的类型和exp一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Foo { public : static const int Number = 0 ; int x; }; int n = 0 ;volatile const int & x = n;decltype (n) a = n; decltype (x) b = n; decltype (Foo::Number) c = 0 ; Foo foo; decltype (foo.x) d = 0 ;
exp是函数调用,推导的类型和返回值类型一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int & func_int_r (void ) ; int && func_int_rr (void ) ; int func_int (void ) ; const int & func_cint_r (void ) ; const int && func_cint_rr (void ) ; const int func_cint (void ) ; const Foo func_cfoo (void ) ; int x = 0 ;decltype (func_int_r ()) a1 = x; decltype (func_int_rr ()) b1 = 0 ; decltype (func_int ()) c1 = 0 ; decltype (func_cint_r ()) a2 = x; decltype (func_cint_rr ())b2 = 0 ; decltype (func_cint ()) c2 = 0 ; decltype (func_cfoo ()) ff = Foo ();
由此可以看出规则2,decltype的结果和函数的返回值类型一致,唯一需要注意的点在,返回值为纯右值的情况下,只有类类型可以携带cv限定符,此外一半忽略cv限定,如c2和ff对比
其他情况,若exp是一个左值,则推导的类型为左值引用,否则和exp类型一致
1 2 3 4 5 6 7 8 9 10 11 struct Foo {int x;};const Foo foo = Foo ();decltype (foo.x) a = 0 ; decltype ((foo.x)) b = a; int n = 0 , m = 0 ;decltype (n + m) c = 0 ; decltype (n +=m) d = c;
a根据规则1推导,标识符的类型也即是int, b,c,d根据规则3推导,a,d为左值,推导为左值引用,c为右值,推导为int
decltype的实际应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 template <class ContainerT >class Foo { typename ContainerT::iterator it_; public : void func (ContainerT& container) { it_ = container.begin (); } ... }; int main () { typedef const std::vecotr<int > container_t ; container_t arr; Foo<container_T> foo; foo.func (arr); return 0 ; } template <class ContainerT >class Foo { decltype (ContainerT ().begin ()) it_; public : void func (ContainerT& container) { it_ = container.begin (); } ... };
3.返回类型后置语法—-auto和decltype的结合使用 这块通过例子理解就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 template <typename R,typename T,typename U>R add (T t, U u) { return t+u; } int a = 1 ; float b = 2.0 ;auto c = add <decltype (a+b)>(a,b);template <typename T,typename U>decltype (t+u) add (T t,U u){ return t+u; } template <typename T,typename U>decltype (T ()+U ()) add (T t,U u){ return t+u; } template <typename T,typename U>decltype ((*(T*)0 )+(*(U*)0 )) add (T t,U u){ return t+u; } template <typename T,typename U>auto add (T t,U u) -> decltype (t+u) { return t+u; }
二.模板的细节改进 1.模板的右尖括号 这里主要是一个细节上的优化,直接看例子就行了
1 2 3 4 5 6 7 8 9 template <class T >struct Foo {int x;};template <class T >class A {int x;};Foo<A<int >> x; Foo<100 >> 2 > x; Foo<(100 >>2 )> x;
2.模板的别名 也没啥好说的 using 替代了 typedef 而且using还能处理模板时的别名,而typedef不行
1 2 3 4 5 6 7 8 9 10 11 12 13 template <typename T>struct func_t { typedef void (*type) (T,T) ; }; func_t <int >::type xx_1;template <typename T>using func_t = void (*)(T,T);func_t <int > xx_2;
3.函数模板的默认参数模板 C++98/03时只支持类模板的默认参数,不支持函数的默认模板参数,C++11中这限制被解除,而且类模板和函数模板有点差异 ,当所有模板参数都有模板时,函数模板的调用如同一个普通函数 ,但类模板即使都有默认参数,还是需要在模板名后跟上”<>”来实例化
这里的一些关键点在于,模板参数的自动填充推导
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 template <typename R = int ,typename U>R func (U val) { ret val; } int main () { func (123 ); return 0 ; } func <long >(123 ); template <typename T>struct identity { typedef T type; }; template <typename T = int >void func (typename identity<T>::type val, T = 0 ){ ... } int main (){ func (123 ); func (123 ,123.0 ); return 0 ; }
显示指定模板参数类型,模板参数的填充顺序,从左往右,第二个例子外敷模板禁用参数的自动推导
三.列表初始化 1.统一的初始化 之前只能用于普通数组和POD类型,现在可以用于所有类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Foo { public : Foo (int ) {} private : Foo (const Foo &); }; int main () { Foo a1 (123 ) ; Foo a2 = 123 ; Foo a3 = {123 }; Foo a4 {123 }; int a5 = {3 }; int a6 {3 }; return 0 ; }
2.列表初始化的使用细节 只能对聚合类型使用
聚合类型是一个普通数组
聚合类型一个类且,无自定义的构造 ,无私有保护非静态数据成员 ,无基类 ,无虚函数 ,不能有直接初始化的数据
对于无自定义的构造这条,可能会觉得不是有构造的时候也可以吗,实际上此时用初始化列表,调用了构造函数,这也是对于非聚合类型,使用初始化列表的方法
3.初始化列表 C++11中的STL容器拥有和未指定长度的数组一样的能力
1 2 3 4 5 6 7 int arr[] {1 ,2 ,3 };std::map<std::string,int > mm= { {"1" ,1 }, {"2" ,2 } , {"3" ,3 } }; std::set<int > ss = {1 ,2 ,3 }; std::vector<int > arr = {1 ,2 ,3 ,4 ,5 };
这种能力来自于 std::initializer_list 这个轻量级的类模板,对于我们自定义的类,只要添加这个,也能拥有这个功能
1 2 3 4 5 6 class Foo { public : Foo (std::initializer_list<int >) {} }; Foo foo = {1 ,2 ,3 ,4 ,5 };
再举出一个使用的例子,给自定义的容器赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class FooVecor { std::vector<int > content_; public : FooVector (std::initializer_list<int > list) { for (auto it = list.begin (); it!=list.end (); ++it) { content_.push_back (*it); } }; } class FooMap { std::map<int ,int > content_; using pair_t = std::map<int ,int >::value_type; public : FooMap (std::initializer_list<pair_t > list) { for (auto it = list.begin (); it!=list.end (); ++it) { content_.insert (*it); } } }; FooVector foo_1 = {1 ,2 ,3 ,4 ,5 }; FooMap foo_2 = {{1 ,2 },{3 ,4 },{5 ,6 }};
std::initializer_list的一些细节
轻量级的容器类型
储存的元素必须是同种类型
3个接口, size begin end
只能被整体初始化或赋值
传递效率高,因为保存的是元素的引用,因为是引用所以也不能,保存局部变量
4.防止类型收窄 看看就好,p33面图
四.基于范围的for循环 1.for循环的新用法 与一般的for遍历,和for_each便利,不同给出了一种新的遍历方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #inclued <iostream> #include <vector> int main () { std::vector<int > arr = {1 ,2 ,3 }; for (auto n : arr) { std::cout << n << std::endl; } for (auto & n : arr) { std::cout << n++ << std::endl; } return 0 ; }
2.基于范围的for循环的使用细节
使用时注意容器本身的约束,如set和map容器是不支持修改的,如果用auto & 则会推到为带const的
在迭代时修改容器会发生什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <iostream> #include <vector> int main () { std::vector<int > arr={1 ,2 ,3 ,4 ,5 }; for (auto val : arr) { std::cout << val << std::endl; arr.push_back (0 ); } return 0 } #include <iostream> #include <vector> int main (){ std::vector<int > arr = {1 ,2 ,3 ,4 ,5 }; auto && _range = (arr); for (auto _begin = _range.begin (),_end=_range.end (); _begin != _end; ++ _begin) { auto val = * _begin; std::cout << val << std::endl; arr.push_back (0 ); } return 0 ; }
3.让基于范围的for循环支持自定义类型
对于普通的array对象,begin为首地址,end为首地址+容器长度
对于类对象,range-based for 查找类的begin() 和 end() 来确定,如同书中的例子
否则采用全局的begin 和 end函数来确定(不清楚这条)
书中给出了一个例子,自己实现,range函数返回一个自己定义的容器,底层由一个迭代器类实现,impl为范围for迭代的容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 #include <iostream> namespace detail_range{ template <typename T> class iterator { public : using value_type = T; using size_type = size_t ; private : size_type cursor_; const value_type step_; value_type value_; public : iterator (size_type cur_start, value_type begin_val, value_type step_val) :cursor_ (cur_start), step_ (step_val), value_ (begin_val) { value_ += (step_ * cursor_); } value_type operator *() const { return value_; } bool operator !=(const iterator& rhs)const { return (cursor_ != rhs.cursor_); } iterator& operator ++() { value_ += step_; ++ cursor_; return (*this ); } }; template <typename T> class impl { public : using value_type = T; using reference = const value_type&; using const_reference = const value_type&; using iterator = const detail_range::iterator<value_type>; using const_iterator = const detail_range::iterator<value_type>; using size_type = typename iterator::size_type; private : const value_type begin_; const value_type end_; const value_type step_; const size_type max_count_; size_type get_adjusted_count () const { if (step_>0 && begin_ >=end_) throw std::logic_error ("End value must be greater than begin value." ); else if (step_<0 && begin_<=end_) throw std::logic_error ("End value must be less than begin value." ); auto x = static_cast <size_type>((end_-begin_)/step_); if (begin_ + (step_*x) != end_) ++x; return x; } public : impl (value_type begin_val, value_type end_val, value_type step_val) :begin_ (begin_val) , end_ (end_val) ,step_ (step_val) , max_count_ (get_adjusted_count ()) {} size_type size () const { return max_count_; } const_iterator begin () const { return {0 , begin_, step_}; } const_iterator end () const { return {max_count_,begin_,step_}; } }; } template <typename T>detail_range::impl<T> range (T end) { return {{},end,1 }; } template <typename T>detail_range::impl<T> range (T begin, T end) { return {begin,end,1 }; } template <typename T,typename U>auto range (T begin,T end, U step) -> detail_range::impl<decltype (begin+step) > { using r_t = detail_range::impl<decltype (begin+step)>; return r_t (begin,end,step); } void test_range () { using std::endl; using std::cout; cout << "Range(15):" ; for (int i : range (15 )) { cout << " " << i; } cout << endl; cout << "Range(2,6):" ; for (auto i : range (2 ,6 )) { cout << " " << i; } cout << endl; const int x = 2 , y = 6 , z = 3 ; cout << "Range(2,6,3):" ; for (auto i : range (x,y,z)) { cout << " " << i; } cout << endl; } int main () { test_range (); return 0 ; }
对此处的设计还是有点不清楚,只大致看懂了实现流程,可能水平不够
五.function和bind绑定器 1.可调用对象
是一个函数指针
是一个具有operator()的函数对象(仿函数)
可被转换成函数指针的类对象(用转换函数实现)
是一个类成员(函数)指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 void func () {...}struct Foo { void operator () { ... } }; struct Bar { using fr_t = void (*)(void ); static void func (void ) { ... } operator fr_t () { return func; } }; struct A { int a_; void mem_func () { ... } }; int main () { void (* func_ptr)(void ) = & func; func_ptr (); Foo foo; foo (); Bar bar; bar (); void (A::*mem_func_ptr)(void ) = &A::mem_func; int A::*mem_obj_ptr = &A::a_; A aa; (aa.*mem_func_ptr)(); aa.*mem_obj_ptr = 123 ; return 0 ; }
2.可调用对象包装器—-std::function 简单来说用来容纳除了类成员指针和类成员函数指针的一个类模板,相当于对上述各种不同的可调用对象,用一个统一的方式管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <functional> void func () { std::cout << __FUNCTION__ << std::endl; } class Foo { public : static int foo_func (int a) { std::cout << __FUNCTION__ << "(" << a << ") ->: " ; return a; } }; class Bar { public : int operator () (int a) { std::cout << __FUNCTION__ << "(" << a << ") ->: " ; return a; } }; int main () { std::function<void (void )> fr1 = func; fr1 (); std::function<int (int )> fr2 = Foo::foo_func; std::cout << fr2 (123 ) << std::endl; Bar bar; fr2 = bar; std::cout << fr2 (123 ) << std::endl; return 0 ; }
std::function需要的只是合适的函数前面,返回值和参数表,即可容纳所有符合的函数
一个回调函数的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <iostream> #include <functional> class A { std::function<void ()> callback_; public : A (const std::function<void ()>& f) :callback_ (f) {} void notify (void ) { callback_ (); } }; class Foo { public : void operator () (void ) { std::cout << __FUNCTION__ << std::endl; } }; int main () { Foo foo; A aa (foo) ; aa.notify (); return 0 ; }
还有一个例子,和上面的差不多,可以看出包装器,比起普通函数指针更加方便灵活
3.std::bind绑定器 主要两个用途 1.将可调用对象及其参数一起绑定成一个函数对象 2.将多元可调用对象,转换成一元或n-1元
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <functional> #include <iostream> void call_when_even (int x, const std::function<void (int )>& f) { if (!(x & 1 )) { f (x); } } void output (int x) { std::cout << x << " " ; } void output_add_2 (int x) { std::cout << x + 2 << " " ; } int main () { { auto fr = std::bind (output, std::placeholders::_1); for (int i = 0 ; i < 10 ; i++) { call_when_even (i, fr); } } std::cout << std::endl; auto fr = std::bind (output_add_2, std::placeholders::_1); for (int i = 0 ; i < 10 ; i++) { call_when_even (i, fr); } system ("pause" ); return 0 ; } #include <iostream> #include <functional> void output (int x,int y) { std::cout << x << " " << y << std::endl; } int main () { std::bind (output,1 ,2 )(); std::bind (output, std::placeholders::_1, 2 )(1 ); std::bind (output, 2 , std::placeholders::_1)(1 ); std::bind (output, 2 , std::placeholders::_2)(1 ); std::bind (output, 2 , std::placeholders::_2)(1 ,2 ); std::bind (output, std::placeholders::_1, std::placeholders::_2)(1 , 2 ); std::bind (output, std::placeholders::_2, std::placeholders::_1)(1 , 2 ); return 0 ; }
其次就是之前提到的和function组合使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <iostream> #include <functional> class A { public : int i_ = 0 ; void output (int x,int y) { std::cout << x << " " << y << std::endl; } }; int main () { A a; std::function<void (int ,int )> fr = std::bind (&A::output,&a, std::placeholders::_1,std::placeholders::_2); fr (1 ,2 ); std::function<int &(void )> fr_i = std::bind (&A::i_,&a); fr_i = 123 ; std::cout << a.i_ << std::endl; return 0 ; }
将A的成员函数output的指针与a绑定,转换成一个仿函数放入包装器
另外两个用途
一起版本的bind1st 和bind2nd 现在可以被,bind用不同位置的占位符取代
使用组合bind函数,如大于5小于10 可以将这两个函数和一个逻辑与函数绑在一起实现
六.lambda表达式 1.lambda 1 [ capture ] (params) opt -> ret { body; };
capture 为捕获方式, params为参数列表 , opt为函数选项, ret为返回类型
如下为一个完整的lambda表达式例子
1 2 3 4 5 6 7 auto f = [](int a) -> int {return a+1 ; };std::cout << f (1 ) << std::endl; auto f = [](int a) {return a+1 ; };auto x1 = [](int i) {return i;};auto x2 = []() { return {1 ,2 } ;};
lambda表达式捕获不同方式:
[] 不捕获任何变量
[&] 按引用捕获所有变量
[=] 按值捕获所有变量
[=,&foo] 按值捕获所有变量,按引用捕获foo变量
[bar] 只按值捕获bar变量
[this] 捕获this指针,lambda拥有和类成员相同的访问权
一个注意点 :按值捕获的变量,在lambda表达式是不能修改的,也就是说以下例子中的用法是不行的
1 2 3 int a = 0 ;auto f1 = [=]{return a++; }; auto f2 = [=]() mutable { return a++; };
其他一些注意的点
可以用std::bind 和 std::function来存储和操作lambda表达式
对于没有捕获任何变量的 lambda表达式,可以被转换成一个普通的函数指针
对于为什么按值捕获的变量不能修改
书中的解释是,lambda和基于范围的for类似,都是一种语法糖,实质还是一个函数对象,且lambda表达式默认的operator()是const的,而捕获的值相当于成员变量,这也是为什么不能修改,而mutable的作用就是取消const
2.声明式的编程风格,简洁的代码 替换掉函数对象的定义,直接就地定义并实现
3.在需要的时间和地点实现闭包,使程序更灵活 如前文std::bind时提到的大于5小于10的函数,此时能够更简洁的实现
1 int count = std::count_if (coll.begin (),coll.end (),[](int x) {return x>5 && x<10 ;});
且增加了可读性,一眼就能看出此处函数的功能以及实现
七.tupe元组 使用上相当于一个普通的结构体,直接看用法就完事了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 tuple<const char *,int > tp = make_tuple (sendPack,nSendSize); struct A { char * p; int len; }; int x = 1 ;int y = 2 ;string s = "aa" ; auto tp = std::tie (x,s,y);const char * data = tp.get <0 >(); int len = tp.get <1 >(); int x,y;string a; std::tie (x,a,y) = tp; std::tie (std::ignore,std::ignore,y) = tp; std::map<int ,std::string> m; m.emplace (std::piecewise_construct,std::forward_as_tuple(10 ), std::forward_as_tuple(20 ,'a' ));
八.总结 本章主要介绍的特性简化代码,auto和decltype的配合使用方便获取返回值类型,模板的别名,方便减少琐碎的代码,列表初始化统一初始化的方式,range for 更简单的遍历容器,function 和 bind 灵活的对函数操作,tupl元组,替代简单的结构体,lambda表达式,就地定义匿名函数,提高可读性
第二章 使用C++11改进程序性能 一.右值引用 C++11新增的类型,右值引用. C++中所有的值都属于左值和右值之一,右值又分为将亡值(x-value)和纯右值
1.&&的特性 因为右值不具名,只能通过引用的方式来找到它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <iostream> using namespace std;int g_constructCount = 0 ;int g_copyConstructCount = 0 ;int g_destructCount = 0 ;struct A { A () { cout << "construct: " << ++g_constructCount << endl; } A (const A& a) { cout << "copy construct: " << ++g_copyConstructCount << endl; } ~A () { cout << "destruct: " << ++g_destructCount << endl; } }; A GetA () { return A (); } int main () { A a = GetA (); return 0 ; } int main () { A&& a = GetA (); return 0 ; }
通过右值引用,减少了一次析构和拷贝构造,利用这个特点做到了性能优化,同样,常量的左值引用也能用来这样
常量的左值引用相当于万能引用 ,既能接受左值也能接受右值,但普通的左值引用只能接收左值
universal reference , 当发生自动推导(模板的类型或auto) , &&的类型不确定
1 2 3 4 5 6 template <typename T>void f (T&& param) ;f (10 ); int x = 10 ; f (x);
这种变化被称为引用折叠:
所有的右值叠加到右值引用上仍然还是右值引用
所有的其它引用类型之间的叠加都将变成左值引用
注意这种引用仅出现在 自动推导 以及 **T&&**下,其他任何一点附带条件都会使之失效,如:
1 2 3 4 template <typename T>void f (std::vector<T>&& param) template <typename T>void f (const T&& param) ;
1 2 3 4 int w1,w2;decltype (w1)&& v2 = w2; decltype (w1)&& v2 = std::move (w2);
注意:编译器会将已命名的右值引用视为左值,而将未命名的右值引用视为右值
2.右值引用优化性能,避免深拷贝 也就是用移动构造,常规的深拷贝是利用拷贝构造重新分配空间,然后复制资源,移动构造则采用转移资源的拥有权,采用浅拷贝的方式,减少不必要的临时对象的创建和销毁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 class MyString { private : char * m_data; size_t m_len; void copy_data (const char *s) { m_data = new char [m_len+1 ]; memcpy (m_data,s,m_len); m_data[m_len] = '\0' ; } public : MyString () { m_data = NULL ; m_len = 0 ; } MyString (const char * p) { m_len = strlen (p); copy_data (p); } MyString (const MyString& str) { m_len = str.m_len; copy_data (str.m_data); } MyString& operator =(const MyString& str) { if (this != &str) { m_len = str.m_len; copy_data (str.m_data); } return *this ; } MyString (MyString&& str) { m_len = str.m_len; m_data = str.m_data; str.m_len = 0 ; str.m_data = nullptr ; } MyString& operator =(MyString&& str) { if (this != &str) { m_len = str.m_len; m_data = str.m_data; str.m_len = 0 ; str.m_data = nullptr ; } return *this ; } ~MyString () { if (m_data) delete [] m_data; } }
二.move语义 仅仅转移资源的所有者,将资源的拥有者改为被赋值者,这就是所谓的move语义
总而言之,就是为了让左值在特定情况下使用移动构造或移动=operator , 来减小不必要的开下,此时就会用到std::move来讲一个左值强制转为一个右值引用
三.forward完美转发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> using namespace std;void PrintT (int & t) { cout << "lvalue" << endl; } template <typename T>void PrintT (T&& t) { cout << "rvalue" << endl; } template <typename T>void TestForward (T&& v) { PrintT (v); PrintT (std::forward<T>(v)); PrintT (std::move (v)); cout << endl; } int main () { TestForward (1 ); int x = 1 ; TestForward (x); TestForward (std::forward<int >(x)); TestForward (std::forward<int &>(x)); system ("pause" ); return 0 ; }
万能函数包装器,后续看完3.2来补充
1 std::cout << "hello,world" << std::endl;
四.emplace_back减少内存拷贝和移动 基本用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <vector> #include <iostream> using namespace std;struct A { int x; double y; A (int a,double b):X (a),y (b) {} }; int main () { vector<A> v; v.emplace_back (1 ,2 ); cout << v.size () << endl; return 0 ; }
对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <vector> #include <map> #include <string> #include <iostream> using namespace std;struct Complicated { int year; double country; string name; Complicated (int a,double b,string c):year (a),country (b),name (c) { cout << "is constructed" <<endl; } Complicated (const Complicated& other):year (other.year),country (other.country), name (std::move (other.name)) { cout << "is moved" <<endl; } }; int main () { map<int ,Complicated> m; int anInt = 4 ; double aDouble = 5.0 ; string aString = "C++" ; cout << "--insert--" <<endl; m.insert (std::make_pair (4 ,Complicated (anInt,aDouble,aString))); cout << "--emplace--" <<endl; m,emplace (4 ,Complicated (anInt,aDouble,aString)); cout << "--emplace_back--" <<endl; vector<Complicated> v; v.emplace_back (anInt,aDouble,aString); cout << "--push_back--" <<endl; v.push_back (Complicated (anInt,aDouble,aString)); }
五.unordered container 无序容器 C++11增加了无序容器,unordered_map / unordered_multimap 和 unordered_set / unordered_multiset,这些容器不排序,因此会比有序版本的效率更高,map和set内部是红黑树,而无序容器内部是散列表,
1 2 std::cout << "hello,world" << std::endl;
六.总结 1.右值引用仅仅是通过改变资源的所有者来避免拷贝内存,能大幅度提高性能
2.forward能根据参数的实际类型转发给正确的函数
3.emplace系列函数通过直接构造对象的方式避免了内存的拷贝和移动
4.无序容器在插入元素时不排序,提高了插入效率,不过对于自定义的key时需要提供hash函数和比较函数
第三章 使用C++11消除重复,提高代码质量 一.type_traits—-类型萃取 在Effective C++中也提到过一次这个,感觉不是很好理解,目前理解就是帮助在编译期一些操作的工具,用到模板的特性来实现?
1.基本的type_tratis
简单的traits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 template <class T >struct GetLeftSize { static const int value = 1 ; }; template <typename T>class GetLeftSize { enum {value=1 }; }; template <typename Type>struct GetLeftSize :std::integral_constant<int ,1 >{}; template <typename T,T t>struct integral_constant { static const T value = t; typedef T value_type; typedef integral_constant<T,t> type; operator value_type () {return value;} };
类型判断的type_traits
1 2 3 4 5 6 7 cout << "----two----" <<endl; cout << "is_const:" << endl; cout << "int: " << std::is_const<int >::value << endl; cout << "const int: " << std::is_const<const int >::value << endl; cout << "const int&: " << std::is_const<const int &>::value << endl; cout << "const int*: " << std::is_const<const int *>::value << endl; cout << "int* const: " << std::is_const<int * const >::value << endl;
判断两个类型之间的关系tratis
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 cout << std::is_same<int ,int >::value << endl; cout << std::is_same<int ,unsigned int >::value << endl; cout << std::is_same<int ,signed int >::value << endl; class A {};class B :public A{};class C {};int main () { cout << std::is_base_of<A,B>::value << endl; cout << std::is_base_of<B,A>::value << endl; cout << std::is_base_of<C,B>::value << endl; } class A {};class B :public A{};class C {};int main () { bool b2a = std::is_convertible<B*,A*>::value; bool a2b = std::is_convertible<A*,B*>::value; bool b2c = std::is_convertible<B*,C*>::value; cout << b2a << endl; cout << a2b << endl; cout << b2c << endl; }
类型转换的traits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <type_traits> #include <memory> using std::cin;using std::cout;using std::endl;using std::is_same;template <class T >struct Construct { typedef typename std::remove_reference<T>::type U; Construct ():m_ptr (new U) { *m_ptr = 999 ; } typename std::add_lvalue_reference<U>::type Get () const { return *m_ptr.get (); } private : std::unique_ptr<U> m_ptr; }; int main () { cout << std::boolalpha; cout << is_same<const int ,std::add_const<int >::type>::value << endl; cout << is_same<int ,std::remove_const<const int >::type>::value << endl; cout << is_same<int &,std::add_lvalue_reference<int >::type>::value << endl; cout << is_same<int &&,std::add_rvalue_reference<int >::type>::value << endl; cout << is_same<int ,std::remove_reference<int &>::type>::value << endl; cout << is_same<int ,std::remove_reference<int &&>::type>::value << endl; cout << is_same<int ,std::remove_extent<int []>::type>::value << endl; cout << is_same<int [2 ],std::remove_extent<int [][2 ]>::type>::value << endl; cout << is_same<int [2 ][3 ],std::remove_extent<int [][2 ][3 ]>::type>::value << endl; cout << is_same<int ,std::remove_all_extents<int [][2 ][3 ]>::type>::value << endl; typedef std::common_type<unsigned char ,short ,int >::type NumericType; cout << is_same<int ,NumericType>::value << endl; Construct<int > c; int a = c.Get (); cout << a++ << endl; cout << a << endl; return 0 ; }
std::decay 对于上述有时候需要移除或添加的可能会比较麻烦,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 template <typename T>typename std::remove_cv<typename std::remove_reference<T>::type>::type * creat_1 () { return new typename std::remove_cv<typename std::remove_reference<T>::type>::type (); } int * p = creat_1 <const int &>();template <typename T>typename std::decay<T>::type * creat_2 () { return new typename std::decay<T>::type (); } int * x = creat_2 <const int *>();typedef std::decay<int >::type A; typedef std::decay<int &>::type B; typedef std::decay<int &&>::type C; typedef std::decay<const int &>::type D; typedef std::decay<int [2 ]>::type E; typedef std::decay<int (int )>::type F;
decay 用法的一个例子,保存函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void OutPut () { cout << "hello,world" << endl; } template <typename F>struct Save_Function { using FnType = typename std::decay<F>::type; explicit Save_Function (F & f) :m_fn(f) { } void Run () { m_fn (); } FnType m_fn; }; int main () { void (*func)() = OutPut; Save_Function<void (*) () > l (func) ; l.Run (); }
2.根据条件选择的tratis 简单好用,直接看例子就行了
1 2 3 4 5 6 7 8 template <bool B,class T ,class F > struct conditional ; typedef std::conditional<true ,int ,float >::type A; typedef std::conditional<false ,int ,float >::type B; typedef std::conditional<std::is_integral<A>::value,long ,int >::type C; typedef std::conditional<std::is_integral<B>::value,long ,int >::type D;
3.获取可调用对象返回类型的tratis 很容易想到的就是auto 和 decltype 结合使用,但有局限性
1 2 3 4 5 6 7 8 9 10 class A { A ()=delete ; public : int operator () (int i) { return i; } }; decltype (A ()(0 )) i = 4 ;
对此可以用到std::declval (能获取任何类型的临时值)
1 2 3 decltype (std::declval <A>()(std::declval <int >())) i = 4 ;
另一种方案就是std::result_of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <class F ,class ... ArgTypes>class result_of <F (ArgTypes...)>;int fn (int ) {return int ();}typedef int (&fn_ref) (int ) ;typedef int (*fn_ptr) (int ) ;struct fn_class { int operator () (int i) {return i;}};int main () { typedef std::result_of<decltype (fn)&(int )>::type A; typedef std::result_of<fn_ref (int )>::type B; typedef std::result_of<fn_ptr (int )>::type C; typedef std::result_of<fn_class (int )>::type D; cout << std::boolalpha; cout << "typedefs of int: " << endl; cout << "A: " << std::is_same<int ,A>::value << endl; cout << "B: " << std::is_same<int ,B>::value << endl; cout << "C: " << std::is_same<int ,C>::value << endl; cout << "D: " << std::is_same<int ,D>::value << endl; }
通过auto 和 decltype已经简化了一次返回值的确定,这个能在某些情况下更近一步简化,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 template <typename Fn>auto GroupBy (const vector<Person>& vt,const Fn& keySlector) ->multimap<decltype (keySlector((Person&)nullptr ),Person) > { typedef decltype (keySlector(*(Person*)nullptr )) key_type ; multimap<key_type,Person> map; std::for_each(vt.begin (),vt.end (), [&](const Person& person) { map.insert (make_pair (keySlector (person),person)); }); return map; } template <typename Fn>multimap<typename std::result_of<Fn(Person)>::type,Person> GroupBy (const vector<Person>& vt,const Fn& keySlector) { typedef std::result_of<Fn (Person)>::type key_type; multimap<key_type,Person> map; std::for_each(vt.begin (),vt.end (), [&](const Person& person) { map.insert (make_pair (keySlector (person),person)); }); return map; }
4.根据条件禁用或启用某种或某些类型tratis 一个规则SFINAE (substitution-failure-is-not-an-error),替换失败并非失败,体现在重载时匹配函数时,匹配到错误的函数不会直接报错,而是继续匹配其它重载
std::enable_if ,根据条件来选择重载函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 template <bool B,class T = void >struct enable_if;#include <iostream> #include <type_traits> using std::cout;using std::endl;using std::cin;template <class T >typename std::enable_if<std::is_arithmetic<T>::value,T>::type foo (T t) { return t; } template <class T >T foo2 (T t,typename std::enable_if<std::is_integral<T>::value,int >::type =0 ) { return t; } template <class T ,class U >T foo22 (T t,U u) { return t; } template <class T ,class = typename std::enable_if<std::is_integral<T>::value>::type>T foo3 (T t) { return t; } template <class T ,class Enable = void >class A;template <class T >class A <T,typename std::enable_if<std::is_floating_point<T>::value>::type> {};template <class T >typename std::enable_if<std::is_arithmetic<T>::value,int >::type foo1 (T t) { cout << t << endl; return 0 ; } template <class T >typename std::enable_if<!std::is_arithmetic<T>::value,int >::type foo1 (T& t) { cout << typeid (T).name () << endl; return 1 ; } int main () { auto r = foo (1 ); auto r1 = foo (1.2 ); foo2 (1 ,2 ); foo3 (1 ); A<double > a; foo1 (123 ); foo1 ("hello" ); return 0 ; }
再看看一个不用std::enable_if和用的例子,可以看出enable_if能实现强大的重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 template <typename T>string ToString (T t) { if (typeid (T) == typeid (int ) || typeid (T) == typeid (double ) typeid (T) == typeid (float ) || typeid (T) == typeid (float )) { std::stringstream ss; ss << value; return ss.str (); } else if (typeid (T) == typeid (string)) { return t; } } template <class T >typename std::enable_if<std::is_arithmetic<T>::value,string>::type ToString (T& t) { return std::to_string (t);}template <class T >typename std::enable_if<!std::is_same<T,string>::value,string>::type ToString (T& t) { return t;}
二.可变参数模板 1.可变参数模板函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <iostream> #include <tuple> #include <initializer_list> using std::cout;using std::endl;using std::cin;template <class ... T>void f (T... args) { cout << sizeof ...(args) << endl; } void print1 () { cout << "empty" << endl; } template <class T ,class ... Args>void print1 (T head,Args... args) { cout << "parameter " << head << endl; print1 (args...); } template <std::size_t I = 0 , typename Tuple>typename std::enable_if<I == std::tuple_size<Tuple>::value>::type print_tp (Tuple t){ } template <std::size_t I = 0 , typename Tuple>typename std::enable_if<I < std::tuple_size<Tuple>::value>::type print_tp (Tuple t){ std::cout << std::get <I>(t) << std::endl; print_tp <I + 1 >(t); } template <typename ... Args>void print2 (Args... args) { print_tp (std::make_tuple (args...)); } template <class T >void print3 (T t) { cout << t << endl; } template <class ... Args>void print3 (Args... args) { int arr[] ={(print3 (args),0 )...}; } template <typename ... Args>void expand (Args... args) { std::initializer_list<int >{([&]{cout << args << endl;}(),0 )...}; } int main () { cout << "-----size--------" << endl; f (1 ,2 ,3 ,4 ,5 ,6 ); cout << "-----print1------" << endl; print1 (1 ,2 ,3 ,4 ,5 ,6 ,7 ,"hello" ); cout << "-----print2------" << endl; print2 (1 ,2 ,3 ,4 ,5 ,6 ); cout << "-----print3------" << endl; print3 (1 ,2 ,3 ,4 ,5 ,"cow" ); cout << "-----expand------" << endl; expand (1 ,2 ,3 ); return 0 ; }
注意type_traits展开时, std::enable_if 的第二参数可以省略,省略的话默认为void
2.可变参数模板类 模板递归和特化的方式展开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 template <typename ... Args> struct sum ; template <typename First,typename ... Rest>struct sum <First,Rest...> { enum {value = sum<First>::value + sum<Rest...>::value}; }; template <typename Last> struct sum <Last> { enum {value = sizeof (Last)}; }; template <> struct sum <> { enum {value = 0 }; };
使用std::integral_constant替换枚举定义
1 2 3 4 5 6 7 8 9 10 11 template <typename ... Args> struct sum3 ;template <typename First,typename ... Rest>struct sum3 <First,Rest...>:std::integral_constant<int ,sum3<First>::value + sum3<Rest...>::value>{ }; template <typename Last>struct sum3 <Last>:std::integral_constant<int ,sizeof (Last)>{ };
继承的方式展开参数包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 template <int ...>struct IndexSeq {};template <int N,int ... Indexes>struct MakeIndexes : MakeIndexes<N-1 ,N-1 ,Indexes...> {};template <int ... Indexes>struct MakeIndexes <0 ,Indexes...>{ typedef IndexSeq<Indexes...> type; };
通过using实现
1 2 3 4 5 6 7 8 template <int N,int ... Indexes>struct make_index : make_index<N-1 ,N-1 ,Indexes...> {};template <int ... Indexes>struct make_index <0 ,Indexes...>{ using type = IndexSeq<Indexes...>; };
汇总
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 #include <iostream> using std::cin;using std::cout;using std::endl;template <typename ... Args> struct sum ;template <typename First,typename ... Rest>struct sum <First,Rest...>{ enum {value = sum<First>::value + sum<Rest...>::value}; }; template <typename Last> struct sum <Last>{ enum {value = sizeof (Last)}; }; template <> struct sum <>{ enum {value = 0 }; }; template <typename First,typename ... Args> struct sum2 ;template <typename First,typename ... Rest>struct sum2 { enum {value = sum<First>::value + sum<Rest...>::value}; }; template <typename Last> struct sum2 <Last>{ enum {value = sizeof (Last)}; }; template <typename ... Args> struct sum3 ;template <typename First,typename ... Rest>struct sum3 <First,Rest...>:std::integral_constant<int ,sum3<First>::value + sum3<Rest...>::value>{ }; template <typename Last>struct sum3 <Last>:std::integral_constant<int ,sizeof (Last)>{ }; template <int ...>struct IndexSeq {};template <int N,int ... Indexes>struct MakeIndexes : MakeIndexes<N-1 ,N-1 ,Indexes...> {};template <int ... Indexes>struct MakeIndexes <0 ,Indexes...>{ typedef IndexSeq<Indexes...> type; }; template <int N,int ... Indexes>struct make_index : make_index<N-1 ,N-1 ,Indexes...> {};template <int ... Indexes>struct make_index <0 ,Indexes...>{ using type = IndexSeq<Indexes...>; }; int main () { cout << "-----sum-----" << endl; cout << sum<char ,int ,double >::value << endl; cout << "-----sum2----" << endl; cout << sum2<char ,int ,double >::value << endl; cout << "-----sum3----" << endl; cout << sum3<int ,double ,char >::value << endl; cout << "-----class---" << endl; using T = MakeIndexes<3 >::type; cout << typeid (T).name () << endl; cout << "-----using---" << endl; using H = make_index<3 >::type; cout << typeid (H).name () << endl; return 0 ; }
3.可变参数模板消除重复代码 两个示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <iostream> using std::cin;using std::cout;using std::endl;template <typename T>void Print (T t) { cout <<t << endl; } template <typename T,typename ... Args>void Print (T t,Args... args) { cout << t; Print (args...); } struct A { int a; A (int x){a=x;} }; struct B { int x, y; B (int xx,int yy){x=xx,y=yy;} }; template <typename T,typename ... Args>T* Instance1 (Args... args) { return new T (args...); } template <typename T,typename ... Args>T* Instance2 (Args&&... args) { return new T (std::forward<Args>(args)...); } int main () { cout << "----1----" << endl; Print (1 ,2 ,3 ,4 ,2 ,5 ); cout << "----2----" << endl; A* pa = Instance1 <A>(1 ); B* pb = Instance1 <B>(1 ,2 ); cout << pa->a << endl; cout << pb->x << pb->y << endl; cout << "----3----" << endl; A* fpa = Instance2 <A>(1 ); B* fpb = Instance2 <B>(1 ,2 ); cout << fpa->a << endl; cout << fpb->x << fpb->y << endl; return 0 ; }
三.可变参数模板和type_traits的综合应用 1.optional的实现 optional 储存一个T类型的值,只有他被初始化后才是有效的,实现了未初始化的概念.
思路 :为了实现这个东西,他要储存一个T类型的值,且不一定要初始化,所以它应该存在一个缓冲区,用以储存这个值,一般会想到采用 char xx[32]; 和 new (xx) MyStruct ,准备一个char数组和placement new 来实现,但一个问题是,齐位问题,可能导致效率问题或出错,对于这个问题可以采用 std::aligned_storage 解决
1 2 3 4 5 6 7 8 template <std::size_t Len, std::size_t Align =>struct aligned_storage;std::aligned_storage<sizeof (T),std::alignment_of<T>::value> std::aligned_storage<sizeof (T),alignof (A)>
这块地方看到另一种placement的用法,之前只会 int *p = new (xx) MyStruct; 这种用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 #include <iostream> #include <type_traits> #include <string> using std::string;namespace example1{ struct A { int avg; A (int a,int b):avg ((a+b)/2 ) {} }; typedef std::aligned_storage<sizeof (A), alignof (A)>::type Aligned_A; void test () { Aligned_A a,b; new (&a) A (10 ,20 ); b=a; std::cout << reinterpret_cast <A&>(a).avg << std::endl; std::cout << reinterpret_cast <A*>(&a)->avg << std::endl; std::cout << (*(((A*)(&a)))).avg << std::endl; } } template <typename T>class optional { using data_t = typename std::aligned_storage<sizeof (T), alignof (T)>::type; public : optional () {} optional (const T& v) { Create (v); } optional (const optional& other) { if (other.IsInit ()) Assign (other); } ~optional () { Destroy (); } template <class ... Args> void Emplace (Args&&... args) { Destroy (); Create (std::forward<Args>(args)...); } bool IsInit () const {return m_hasInit;} explicit operator bool () const { return IsInit (); } const T& operator *()const { if (IsInit ()) { return *((T*)(&m_data)); } throw std::logic_error ("is not init" ); } private : template <class ... Args> void Create (Args&&... args) { new (&m_data) T (std::forward<Args>(args)...); m_hasInit = true ; } void Destroy () { if (m_hasInit) { m_hasInit = false ; ((T*)(&m_data))->~T (); } } void Assign (const optional& other) { if (other.IsInit ()) { Copy (other.m_data); m_hasInit = true ; } else { Destroy (); } } void Copy (const data_t & val) { Destroy (); new (&m_data) T (*((T*)(&val))); } private : bool m_hasInit = false ; data_t m_data; }; struct MyStruct { MyStruct (){} MyStruct (int a,int b):m_a (a),m_b (b){} int m_a; int m_b; friend std::ostream& operator <<(std::ostream& os,const MyStruct& temp); }; std::ostream& operator <<(std::ostream& os,const MyStruct& temp) { os << " m_a:" << temp.m_a << " m_b:" << temp.m_b; return os; } void test () { optional<string> a ("ok" ) ; optional<string> b ("ok" ) ; optional<string> c ("aa" ) ; c = a; optional<MyStruct> op; op.Emplace (1 ,2 ); MyStruct t; if (op) t = *op; op.Emplace (3 ,4 ); t = *op; std::cout << *a << *op << t << std::endl; optional<int > xx; std::cout << *xx; } int main () { test (); return 0 ; }
2.惰性求值类lazy的实现 C++没有的东西,不过可以自己实现,应用场景举个例子,初始化对象时,对象很大,创建需要较长时间,可以并不需要立马获取大量数据,在使用时获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class LargeObject { public int InitializedBy { get {return intiBy;} } int initBy = 0 ; public LargeObject (int initializedBy ) { initBy = initializedBy; Console.WriteLine("LargeObject was created on thread id {0}." ,initBy); } public long [] Data = new long [1000000 ]; } class TestLazy { Lazy<LargeObject> lazyLargeObject = null ; public TestLazy () { lazyLargeObject = new Lazy<LargeObject>(InitLargeObject); } public void ReallyLoad () { lazyLargeObject.Value; Console.WritenLine("Lazy load big object" ); } } void test (){ TestLazy t = new TestLazy(); t.ReallyLoad(); }
整体思路比较简单,利用lambda的特性将传入的函数和值一起保存起来(将值捕获),真正加载的时候再调用函数,有一点值得注意,Lazy类的构造中,书中是左值引用,但辅助函数中为了做到提高效率采用完美转发,但传入的是右值引用,构造中的左值引用不能接受右值引用,会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 #include <iostream> #include <functional> #include <memory> namespace op{ template <typename T> class optional { using data_t = typename std::aligned_storage<sizeof (T), alignof (T)>::type; public : optional () {} optional (const T& v) { Create (v); } optional (const optional& other) { if (other.IsInit ()) Assign (other); } ~optional () { Destroy (); } template <class ... Args> void Emplace (Args&&... args) { Destroy (); Create (std::forward<Args>(args)...); } bool IsInit () const {return m_hasInit;} explicit operator bool () const { return IsInit (); } T& operator *()const { if (IsInit ()) { return *((T*)(&m_data)); } throw std::logic_error ("is not init" ); } private : template <class ... Args> void Create (Args&&... args) { new (&m_data) T (std::forward<Args>(args)...); m_hasInit = true ; } void Destroy () { if (m_hasInit) { m_hasInit = false ; ((T*)(&m_data))->~T (); } } void Assign (const optional& other) { if (other.IsInit ()) { Copy (other.m_data); m_hasInit = true ; } else { Destroy (); } } void Copy (const data_t & val) { Destroy (); new (&m_data) T (*((T*)(&val))); } private : bool m_hasInit = false ; data_t m_data; }; } using namespace op;template <typename T>struct Lazy { Lazy () {} template <typename Func,typename ... Args> Lazy (Func&& f,Args&&... args) { m_func = [&f,&args...]{return f (args...);}; } T& Value () { if (!m_value.IsInit ()) { m_value = m_func (); } return *m_value; } bool IsValueCreated () const { return m_value.IsInit (); } private : std::function<T()> m_func; optional<T> m_value; }; template <class Func ,typename ... Args>Lazy<typename std::result_of<Func (Args...)>::type> lazy (Func&& fun,Args&&... args) { return Lazy<typename std::result_of<Func (Args...)>::type>(std::forward<Func>(fun),std::forward<Args>(args)...); } struct BigObject { BigObject () { std::cout << "lazy load big object " << std::endl; } }; struct MyStruct { MyStruct () { m_obj = lazy ([]{return std::make_shared <BigObject>();}); } void load () { m_obj.Value (); } Lazy<std::shared_ptr<BigObject>> m_obj; }; int Foo (int x) { return x * 2 ; } void test2 () { int y = 4 ; auto lazyer1 = lazy (Foo,y); std::cout << lazyer1.Value () << std::endl; Lazy<int > lazyer2 = lazy ([]{return 12 ;}); std::cout << lazyer2.Value () << std::endl; std::function<int (int )> f = [](int x){return x+3 ;}; auto lazyer3 = lazy (f,3 ); std::cout << lazyer3.Value () << std::endl; MyStruct t; t.load (); } int main () { test2 (); return 0 ; }
3.dll帮助类 不是太清楚dll是啥,对这里dll的理解,貌似就是一个函数库,放了很多函数,来看点例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void TestDll () { typedef int (*pMax) (int a,int b) ; typedef int (*pGet) (int a) ; HINSTANCE hDll = LoadLibrary ("MyDll.dll" ); if (hMode == nullptr ) return ; pMax Max = (pMax)GetProAddress (hDll,"Max" ); if (Max == nullptr ) return ; int ret = Max (5 ,8 ); pGet Get = (pGet)GetProAddress (hDll,"Get" ); if (Get == nullptr ) return ; int rea = Get (5 ); FreeLibrary (hDll); }
虽然不是很清楚dll是啥,但大概是干啥是知道的,通过dll根据函数名称,获取函数的地址,然后传给对应定义的指针,很显然,上述方法很麻烦,不仅要根据不同的函数,先定义不同的函数指针类型,还要获取之后再调用,步骤繁琐,对此本章的目的就是凭借可变参数模板和 type_traits 解决上述问题
对此我们希望将定义 获取函数 计算结果 整合为一个整体,类似如下
1 2 3 auto ret = CallDllFunc (const string& funName, T arg);
思路: 封装成 getFunction 和 excuteFunction ,
函数的返回值可能是不同类型,如何以一种通用的返回值来消除这个不同返回值导致的差异呢?
函数的入参数目可能任意数目,且类型也不相同,如何来消除入参个数和类型的差异呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <typename T>std::function<T> GetFunction (const string& funcName) { FARPROC funAddress = GetProAddress (m_hMod,funcName.c_str ()); return std::function <T>((T*)(funAddress)); } auto pMax = GetFunction <int (int ,int )>("Max" );auto pGet = GetFunction <int (int )>("Get" );template <typename T,typename ... Args>typename std::result_of<std::functon <T>(Args...)>::type ExecuteFunc (const string& funcName,Args&&... args) { return GetFunction <T>(funcName)(args...); } auto max = ExecuteFunc <int (int ,int )>("Max" ,5 ,8 );auto get = ExecuteFunc <int (int )>("Get" ,5 );
来点完整代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 #include <windows.h> #include <string> #include <map> #include <functional> using namespace std;class DllParser { public : DllParser ():m_hMod (nullptr ) { } ~DllParser () { UnLoad (); } bool Load (const string& dllPath) { m_hMod = LoadLibraryA (dllPath.data ()); if (nullptr == m_hMod) { printf ("LoadLibrary failed\n" ); return false ; } } bool UnLoad () { if (m_hMod == nullptr ) return true ; auto b = FreeLibrary (m_hMod); if (!b) return false ; m_hMod = nullptr ; return true ; } template <typename T> std::function<T> GetFunction (const string& funcName) { auto it = m_map.find (funcName); if (it == m_map.end ()) { auto addr = GetProcAddress (m_hMod,funcName.c_str ()); if (!addr) return nullptr ; m_map.insert (std::make_pair (funcName,addr)); it = m_map.find (funcName); } return std::function <T>((T*)(it->second)); } template <typename T,typename ... Args> typename result_of<function <T>(Args...)>::type ExecuteFunc (const string& funcName,Args&&... args) { auto f = GetFunction <T>(funcName); if (f == nullptr ) { string s = "can not find this function " + funcName; throw s; } return f (forward<Args>(args)...); } private : HMODULE m_hMod; map<string,FARPROC> m_map; };
4.lambda链式调用 首先是PLL 的一个例子,和DLL一样不太清楚PLL是啥
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int main () { auto t = creat_task ([]()->int { return 0 ; }); auto increment = [](int n) {return n+1 ;} int result = t.then (increment).then (increment).then (increment).get (); wcout << result << endl; }
很明显能看出链式的意义
下面就是具体怎么实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <functional> #include <type_traits> using namespace std;template <typename T>class Task ;template <typename R,typename ... Args>class Task <R (Args...)>{ public : Task (function<R (Args...)>&& f):m_fn (move (f)){} Task (function<R (Args...)>& f):m_fn (f){} R Run (Args&&... args) { return m_fn (forward<Args>(args)...); } template <typename F> auto Then (F&& f) ->Task<typename result_of<F (R) >::type (Args...) > { using return_type = typename result_of<F (R)>::type; auto func = move (m_fn); return Task <return_type (Args...)>([func,f](Args&&... args) { return f (func (forward<Args>(args)...)); }); } private : function<R(Args...)> m_fn; }; void TestTask () { Task<int (int ) > task ([](int i){return i;}) ; auto result = task.Then ([](int i){return i+1 ;}). Then ([](int i){return i+2 ;}).Then ([](int i){return i+3 ;}) .Run (1 ); cout << result << endl; } int main () { TestTask (); return 0 ; }
5.any类的实现 应用部分先停一下,先往后看,以后再来回顾
四.总结 C++11进一步增强了C++泛型编程的能力, type_traits 使我们可以在编译期获取对类型进行查询,计算,判断,转换和选择,可以在编译期检测到输入参数类型的正确性,还能实现更为强大的重载。可变参数模板, 大大减少重复的模板定义,增强了模板的功能,可以写出更泛化的代码
第四章 使用C++11解决内存泄漏的问题 一.shared_ptr 共享的智能指针 该智能指针使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候内存,才会被释放,这样就不会导致重复析构同一块内存
1.shared_ptr 的基本用法 1.初始化
初始化的各种方式,通过构造函数,std::make_shared辅助函数和reset方法来初始化.
1 2 3 4 5 6 7 8 9 10 std::shared_ptr<int > p (new int (1 )) ; std::shared_ptr<int > p2 = p; std::shared_ptr<int > ptr; ptr.reset (new int (1 )); if (ptr) { std::cout << "ptr is not null !" << std::endl; }
对于一个未初始化的智能指针,可以通过reset方法来初始化,当智能指针中有值时,调用reset会使引用计数减1
2.获取原始指针
1 2 std::shared_ptr<int > ptr (new int (1 )) ;int * m = ptr.get ();
3.指定删除器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void DeleteIntPtr (int *p) { delete p; } template <typename T>std::shared_ptr<T> make_shared_array (size_t size) { return std::shared_ptr <T>(new T[size],std::default_delete <T[]>()); } int main () { std::shared_ptr<int > p1 (new int , DeleteIntPtr) ; std::shared_ptr<int > p2 (new int [10 ],[](int * p){delete []p;}) ; std::shared_ptr<int > p3 (new int [10 ],std::default_delete<int []>()) ; std::shared_ptr<int > p4 = make_shared_array <int >(10 ); std::shared_ptr<char > p = make_shared_array <char >(10 ); return 0 ; }
2.使用shared_ptr 需要注意的问题
不要用一个原始指针初始化多个shared_ptr
1 2 3 4 int * ptr = new int ;std::shared_ptr<int > p1 (ptr) ;std::shared_ptr<int > p2 (ptr) ;
不要在函数实参中创建shared_ptr
1 2 3 4 5 function (shared_ptr <int >(new int ),g ());shared_ptr<int > p (new int ) ;f (p,g ());
返回this指针,可能导致重复析构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct A { shared_ptr<A>GetSelf () { return shared_ptr <A>(this ); } }; int main () { shared_ptr<A> sp1 (new A) ; shared_ptr<A> sp2 = sp1->GetSelf (); return 0 ; }
感觉上应该根第一个问题差不多,都是用一个原始指针创建了两个智能指针,导致引用计数出现问题,重复析构
1 2 3 4 5 6 7 8 9 10 class A :public std::enable_shared_from_this<A>{ std::shared_ptr<A> GetSelf () { return shared_from_this (); } }; std::shared_ptr<A> spy (new A) ;std::shared_ptr<A> p = spy->GetSelf ();
循环引用问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct A ;struct B ;struct A { std::shared_ptr<B> bptr; ~A () { std::cout << "A is deleted!" << std::endl; } }; struct B { std::shared_ptr<A> aptr; ~B () { std::cout << "B is delted!" << std::endl; } }; void testPtr () { { std::shared_ptr<A> ap (new A) ; std::shared_ptr<B> bp (new B) ; ap->bptr = bp; bp->aptr = ap; } }
很好理解为什么会出现这种问题,我们知道指向同一内存的智能指针会根据引用计数来析构,此时计数都为2,离开作用域时,引用计数减1,但仍不为0,所以不会调用析构函数
二.unique_ptr 独占的智能指针 与shared_ptr 相反,它不允许其它的智能指针共享其内部的指针,不允许通过赋值,将一个unique_ptr赋值给另一个
1 2 unique_ptr<T> myPtr (new T) ;unique_ptr<T> myOtherPtr = myPtr;
但是允许移动
1 2 3 unique_ptr<T> myPtr (new T) ;unique_ptr<T> myOtherPtr = std::move (myPtr); unique_ptr<T> ptr = myPtr;
C++11中没有提供make_unique方法,对此可以自己实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 template <class T ,class ... Args> inline typename std::enable_if<!std::is_array<T>::value,std::unique_ptr<T>>::type make_unique (Args&&... args) { return std::unique_ptr <T>(new T (std::forward<Args>(args)...)); } template <class T > inline typename std::enable_if<std::is_array<T>::value && std::extent<T>::value==0 ,std::unique_ptr<T>>::type make_unique (size_t size) { typedef typename std::remove_extent<T>::type U; return std::unique_ptr <T>(new U[size]()); } template <class T ,class ... Args>typename std::enable_if<std::extent<T>::value!=0 ,void >::type make_unique (Args&&...) = delete ;
对于普通指针,直接创建unique_ptr,对于数组先判断是否为定长数组
1 2 3 make_unique <T[10 ]>(10 ); meke_unique <T[]>(10 );
另一个与shared_ptr的区别,unique_ptr可以指向数组
1 2 std::unique_ptr<int []> ptr (new int [10 ]) ;ptr[9 ] = 9 ;
而std::shared_ptr<int[]> prt(new int[10]);是不合法的. C++17后应该支持了,所以有可能也不会报错
指定删除器时的差别
1 2 3 4 5 6 7 8 std::shared_ptr<int > ptr (new int (1 ), [](int * p){ delete p;}) ;std::unique_ptr<int ,void (*) (int *) > prt (new int (1 ), [](int * p){ delete p;}) ;std::unique_ptr<int ,void (*) (int *) > pt (new int (1 ), [&](int * p){ delete p;}) ;std::unique_ptr<int ,std::function<void (int *)>> pt (new int (1 ), [&](int * p){ delete p;});
或者这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <memory> #include <functional> struct MyDeleter { void operator () (int * p) { std::cout << "delete" << std::endl; delete p; } }; int main () { std::unique_ptr <int , MyDeleter> p (new int (1 )); return 0 ; }
三. weak_ptr 弱引用的智能指针 主要用来协助shared_ptr ,用来监视shared_ptr , 不会使引用计数+1
1.weak_ptr 的基本用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <memory> std::weak_ptr<int > b3; void f () { if (b3.expired ()) { std::cout << "b3 is expired " << std::endl; } else { auto ret = b3.lock (); std::cout << *ret << std::endl; } } int main () { std::shared_ptr<int > p1 (new int (10 )) ; std::weak_ptr<int > b1 (p1) ; std::cout << b1.use_count () << std::endl; std::shared_ptr<int > p2 (new int (10 )) ; std::weak_ptr<int > b2 (p2) ; if (b2.expired ()) std::cout << "already been deleted " << std::endl; else std::cout << "not yet been deleted " << std::endl; { auto p3 = std::make_shared <int >(42 ); b3 = p3; f (); } return 0 ; }
2.weak_ptr 返回this指针 1 2 3 4 5 6 7 8 9 10 11 12 13 struct A :public std::enable_shared_from_this<A>{ std::shared_ptr<A> GetSelf () { return shared_from_this (); } ~A () { std::cout << "A is deleted " << std::endl; } }; std::shared_ptr<A> spy (new A) ;std::shared_ptr<A> p = spy -> GetSelf ();
shared_from_this 通过内部的weak_ptr调用lock方法之后返回智能指针
3.weak_ptr 解决循环引用问题 weak_ptr 只是观测 不会增加引用计数,所以bp的引用计数为1,析构时减为0,执行析构的同时将成员A析构,这样A的引用计数也变为1了,A也顺利执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> #include <memory> struct A ;struct B ;struct A { std::weak_ptr<B> bptr; ~A () { std::cout << "A is deleted " << std::endl; } }; struct B { std::shared_ptr<A> aptr; ~B () { std::cout << "B is deleted " << std::endl; } }; void testPtr () { { std::shared_ptr<A> ap (new A) ; std::shared_ptr<B> bp (new B) ; ap->bptr = bp; bp->aptr = ap; } } int main () { testPtr (); return 0 ; }
四. 通过智能指针管理第三方库分配的内存 五. 总结 智能指针是解决内存泄漏问题的利器,但应该注意一些使用上的细节
shared_ptr 和 unique_ptr 如何选择
weak_ptr 和 shared_ptr 的关系, 使用weak_ptr 来解决 shared_ptr 使用中出现的问题,循环引用或是返回this指针
第五章 使用C++11让多线程开发变得简单 第六章 使用C++11中便利的工具 一.处理日期和时间的chrono库 主要是三种类型:时间间隔duration 时钟clocks 和 时间点time point
1.记录时长的duration 记录时间长度的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <class Rep ,class Period = std::ratio<1 ,1 > > class duration; template <std::intmax_t Num,std::intmax_t Denom = 1 > class ratio; typedef duration <Rep,ratio<3600 ,1 > > hours;typedef duration <Rep,ratio<60 ,1 > > minutes;typedef duration <Rep,ratio<1 ,1 > > seconds;typedef duration <Rep,ratio<1 ,1000 > > millisecond;typedef duration <Rep,ratio<1 ,1000000 > > microseconds;typedef duration <Rep,ratio<1 ,1000000000 > > nanoseconds;std::this_thread::sleep_for (std::chrono::seconds (3 )); std::this_thread::sleep_for (std::chrono::milliseconds (100 )); std::chrono::milliseconds ms {3 }; std::chrono::microseconds us = 2 * ms; std::cout << "3 ms duration has " << ms.count () << " ticks\n" << "6000 us duration has " << us.count () << " ticks\n" ;
另一个值得注意的就是,duraion之间的运算,对于不同时钟周期的duration会先统一成一种时钟,再做加减运算,规则如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void TestChrono () { std::chrono::duration<double ,std::ratio<9 ,7 >> d1 (3 ); std::chrono::duration<double ,std::ratio<6 ,5 >> d2 (1 ); auto d3 = d1 -d2; std::cout << typeid (d3).name () << std::endl; std::cout << d3.count () << std::endl; }
2.表示时间点的time point 3.获取时钟的clocks 4.计时器timer 前面2,3我的编译器上跑不了,先忽略了先,这个计时器还是蛮不错的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <chrono> #include <iostream> using namespcae std;using namespace std::chrono;class Timer { public : Timer ():m_begin (high_resolution_clock::now ()){} void reset () {m_begin = high_resolution_clock::now ();} template <typename Duration = milliseconds > int64_t elapsed ()const { return duration_cast <Duration>(high_resolution_clock::now ()-m_begin).count (); } int64_t elapsed_nano () const { return elapsed <nanoseconds>(); } int64_t elapsed_micro () const { return elapsed <microseconds>(); } int64_t elapsed_seconds () const { return elapsed <seconds>(); } int64_t elapsed_minutes () const { return elapsed <minutes>(); } int64_t elapsed_hours () const { return elapsed <hours>(); } private : time_point<high_resolution_clock> m_begin; }; void fun () { std::cout << "hello,world " << std::endl; } int main () { Timer t; fun (); std::cout << t.elapsed () << std::endl; std::cout << t.elapsed_micro () << std::endl; std::cout << t.elapsed_nano () << std::endl; std::cout << t.elapsed_seconds () << std::endl; std::cout << t.elapsed_minutes () << std::endl; std::cout << t.elapsed_hours () << std::endl; return 0 ; }
二.数值类型和字符串类型的相互转换 三.宽窄字符转换 编译器上跑不了,目前用不到,先放一放
四.总结 利用工具类和函数提高开发的效率
chrono可以方便的获取时间间隔和时间点,配合一些辅助方法还可以输出格式化时间
to_string 和 atoi/atof 等方法可以方便地实现数值和字符串的相互转换
wstring_convert方便地实现宽窄字符转换
第七章 C++11的其他特性 一.委托构造函数和继承构造函数 1.委托构造函数 1 2 3 4 5 6 7 8 class A { A (){}; A (int a):A (){}; A (int a,int b):A (a){}; };
2.继承构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct Base { int x; double y; string s; Base (int i):x (i),y (0 ){} Base (int i,double j):x (i),y (j){} Base (itn i,double j,const string& str):x (i),y (j),s (str){} }; struct Derived : Base{ }; int main () { Derived d (1 ,2.5 ,"ok" ) ; } struct Derived : Base{ using Base::Base; };
3.原始的字面量 原始字面量可以直接表示字符串的实际含义,因为有些字符串带特殊字符,比如转义字符,需要特殊处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> #include <string> using namespace std;int main () { string str = "D:\A\B\test.text" ; cout << str << endl; string str1 = "D:\\A\\B\\test.text" ; cout << str1 << endl; string str2 = R"(D:\A\B\test.text)" ; cout << str2 << endl; return 0 ; } D:AB est.text D:\A\B\test.text D:\A\B\test.text #include <string> #include <iostream> using namespace std;int main () { string str = R"test(D:\A\B\test.text)"; //error 括号前面和后面的字符串不匹配 string str1 = R"test(D:\A\B\test.text)testaa"; string str2 = R"test(D:\A\B\test.text)test" ; cout << str2 << endl; return 0 ; }
4.final 和 override关键字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 struct A { virtual void foo () final ; void bar () final ; }; struct B final :A { void foo () ; }; struct C : B {}; struct A { virtual void func () {} }; struct D :A{ void func () override { } };
二.内存对齐
内存对齐介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct MyStruct { char a; int b; short c; long long d; char e; } struct MyStruct { char a; char pad_0[3 ]; int b; short c; char pad_0[6 ]; long long d; char e; char pad_0[7 ]; }
堆内存的对齐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <assert.h> inline void * aligned_malloc (size_t size, size_t alignment) { assert (!(alignment & (alignment-1 ))); size_t offset = sizeof (void *) + (--alignment); char * p = static_cast <char *>(malloc (offset + size)); if (!p) return nullptr ; void * r = reinterpret_cast <void *>(reinterpret_cast <size_t > (p + offset) & (!alignment)); static_cast <void *>(r)[-1 ] = p; return r; } inline void aligned_free (void * p) { free (static_cast <void **>(p)[-1 ]); }
利用alignas指定内存对齐大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 alignas (32 ) long long a = 0 ;#define XX 1 struct alignas (XX) MyStruct_1 {}; template <size_t YY = 1 >struct alignas (YY) MyStruct_2 {}; static const unsigned ZZ = 1 ; struct alignas (ZZ) MyStruct_3 {}; _Pragma ("pack(1)" );struct MyStuct { char a; int b; short c; long long d; char e; }; _Pragma ("Pack()" );alignas (int ) char c;
利用alignof和std::alignment_of获取内存对齐大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 MyStruct xx; std::cout << alignof (xx) << std::endl; std::cout << alignof (MyStruct) << std::endl; struct MyStruct { char a; int b; double c; }; int main () { int allignsize = std::alignment_of<MyStruct>::value; int sz = alignof (MyStuct); return 0 ; }
内存对齐类型std::aligned_storage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <std::size_t Len, std::size_t Align = >struct aligned_storage;#include <type_traits> struct A { int avg; A (int a,int b):avg ((a+b)/2 ){} }; typedef std::aligned_storage<sizeof (A),alignof (A)>::type Aligned_A;int main () { Aligned_A a,b; new (&a) A (10 ,20 ); b = a; std::cout << reinterpret_cast <A&>(b).avg << std::endl; return 0 ; }
std::max_align_t 和 std::align 操作符
1 2 3 4 5 6 7 std::cout << alignof (std::max_align_t ) << std::endl; char buffer[] = "--------------------------" ;void * pt = buffer;std::size_t space = sizeof (buffer)-1 ; std::align (alignof (int ), sizeof (char ), pt, space);
三.C++11新增的便利算法 algorithm中新增的算法,用的时候在看看就行了
all_of ,any_of 和none_of
find_if_not
copy_if
iota
is_sorted和is_sorted_until