稳定性与兼容性 断言 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #ifdef  NDEBUG #define  assert(expr)    (static_cast<void> (0)) #else  #endif  #ifndef  _COMPLEX_H #error  "Nerver use <bits/cmathcalls.h> directly; include <complex.h> instead."  #endif  #define  assert_static(e) \     do { \         enum { assert_static__ = 1/(e) }; \     } while (0) 
 
异常 如果noexcept修饰的函数抛出了异常,编译器可用选择直接调用std::terminate()函数来终止程序的运行,如析构函数不应该抛出异常,所以默认是noexcept(true)。在c++98中,使用throw()来声明不抛出异常的函数。
1 2 template  <class  T >void  fun ()  noexcept (noexcept (T()))   {} 
 
初始化 初始化列表的效果总是优先于就地初始化的。 非常量的静态成员变量,需要在头文件以外定义,这会保证编译时,类静态成员的定义最后只存在于一个目标文件中。
sizeof 1 2 3 4 5 6 7 struct  People  {public :    int  hand;     static  People * all; }; sizeof (((People*)0 )->hand); sizeof (People::hand); 
 
友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Poly ;typedef  Poly P;class  LiLei  {    friend  class  Poly ;  }; class  LiLei  {    friend  Poly;  }; class  LiLei  {    friend  P;  }; class  P ;template <typename  T>class  People  {    friend  T; }; 
 
final/override 如果不想成员函数被重载,可以直接将成员函数定义为非虚的。final通常只在继承关系的中途终止派生类的重载有意义。 派生类在虚函数声明中使用了override描述符,那么该函数必须重载其基类中的同名函数,否则编译不过。
模板函数的默认模板参数 1 2 3 4 5 void  DefParm (int  m = 3 )   {} template  <typename  T = int > class  DefClass {};template  <typename  T = int > void  DefTempParm () {};
 
不按照从右往左定义默认类模板参数的模板类都无法通过编译,而函数模板默认模板参数位置则比较随意。
外部模板  可以使用强制实例化进行外部声明。 不使用外部模板声明并不会导致问题,因为只是代码重复,不是数据重复,这是一种对编译时间和空间的优化手段。
通用特性 继承构造函数 如果一个继承构造函数不被相关代码使用,编译器不会为其产生真正的函数代码。
1 2 3 4 5 6 7 8 9 10 11 struct  A  {    A(int  i) {}     A(double  d, int  i) {}     A(float  f, int  i, const  char  * c) {}      }; struct  B  :  A {    using A::A;      virtual void  ExtraInterface ()  {} }; 
 
参数默认值会导致多个构造函数版本的产生。
1 2 3 4 5 6 7 8 9 10 11 12 struct  A  {    A  (int  a = 3 , double  b = 2.4 ) {} }; struct  B  : A {    using  A::A;                          }; 
 
可以通过显示定义继承类的冲突的构造函数,阻止隐式生成相应的继承构造函数来解决冲突。
1 2 3 4 5 6 7 8 struct  A  { A (int ) {} };struct  B  { B (int ) {} };struct  C  : A, B {    using  A::A;     using  B::B;     C (int ) {}  }; 
 
一旦使用了继承构造函数,编译器就不会再为派生类生成默认构造函数了。
1 2 3 4 struct  A  { A (int ) {} };struct  B  : A { using  A::A; };B b;  
 
委派构造函数 黑客版:
1 2 3 Info () { InitRest (); }Info (int  i) { new  (this ) Info (); type = i; } Info (char  e) { new  (this ) Info (); name = e; }
 
所谓委派构造,指的是委派函数将构造的任务委派给了目标构造函数来完成的类构造方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class  Info  {public :    Info () { InitRest (); }     Info (int  i) : Info () { type = i; }      Info (char  e) : Info () { name = e; } private :    void  InitRest ()   { }     int  type {1 };     char  name {'a' }; }; class  Info  {public :    Info () : Info (1 , 'a' ) { }     Info (int  i) : Info (i, 'a' ) { }     Info (char  e) : Info (i, e) { } private :    Info (int  i, char  e) : type (i), name (e) { }     int  type;     char  name; }; 
 
目标构造函数的执行总是先于委派构造函数,如果委派构造函数中使用try,从目标构造函数中产生的异常,都可以在委派构造函数中被捕捉到。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  DCExcept  {public :    DCExcept (double  d)         try  : DCExcept (1 , d) {             cout << "Run the body."  << endl;         }         catch  (...) {             cout << "caught exception"  << endl;         } private :    DCExcept (int  i, double  d) {         cout << "going to throw"  << endl;         throw  0 ;     }     int  type;     double  data; }; 
 
范型编程版:
1 2 3 4 5 6 7 8 9 10 class  TDConstructed  {    template <class  T> TDConstructed (T first, T last)  : l(first, last) { }     list<int > l; public :    TDConstructed (vector<short > &v):             TDConstructed (v.begin (), v.end ()) {}     TDConstructed (deque<int > &d):             TDConstructed (d.begin (), d.end ()) {} }; 
 
右值引用 移动语义 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 #include  <iostream>  using  namespace  std;class  HasPtrMem  {public :    HasPtrMem () : d (new  int (0 )) {         cout << "Construct: "  << ++n_str << endl;     }     HasPtrMem (const  HasPtrMem &h) : d (new  int (*h.d)) {         cout << "Copy constructor: "  << ++n_str << endl;     }     ~HasPtrMem () {         count << "Destruct: "  << ++n_dstr << endl;     }     int  *d;     static  int  n_cstr;     static  int  n_dstr;     static  int  n_cptr; }; int  HasPtrMem::n_cstr = 0 ;int  HasPtrMem::n_dstr = 0 ;int  HasPtrMem::n_cptr = 0 ;HasPtrMem GetTemp ()   {   return  HasPtrMem (); }int  main ()   {    HasPtrMem a = GetTemp (); } 
 
 移动构造函数:
1 2 3 4 HasPtrMem (HasPtrMem &&h) : d (h.d) {    h.d = nullptr ;      cout << "Move construct: "  << ++n_mvtr << endl; } 
 
由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。
1 2 3 T && a = ReturnRvalue ();  T b = ReturnRvalue ();  const  T &f = ReturnRvalue (); 
 
std::move并不移动任何东西,只是将一个左值强制转化为右值引用,继而通过右值引用使用该值,以用于移动语义。
1 static_cast <T&&>(lvalue)
 
移动语义一定是要修改临时变量的值,以下会使得临时变量常量化,成为一个常量右值,使得临时变量的引用也无法修改,导致无法实现移动语义。
1 2 Moveable (const  Moveable&&)const  Moveable ReturnVal ()  ;
 
应该尽量编写不抛出异常的移动构造函数。std::move_if_noexcept在类的构造函数没有noexcept关键字修饰时返回一个左值引用使变量可以实现拷贝语义。
1 2 3 4 5 6 7 8 9 10 11 12 struct  Nothow  {    Nothow () {}     Nothow (Nothow&&) noexcept  {         std::cout << "Nothow move constructor"  << std::endl;     }     Nothow (const  Nothow&) {         std::cout << "Nothow move constructor"  << std::endl;     } }; Nothrow n; Nothrow nt = move_if_noexcept (n); 
 
完美转发 引用折叠规则:一旦定义中出现了左值引用,引用折叠总是优先将其折叠为左值引用。
1 2 3 typedef  const  int  T;typedef  T& TR;TR& v = 1 ; 
 
 完美转发实现过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void  IamForwording (X& &&t)   {    IrunCodeActually (static_cast <X& &&>(t)); } void  IamForwording (X& t)   {    IrunCodeActually (static_cast <X&>(t)); } void  IamForwording (X&& &&t)   {    IrunCodeActually (static_cast <X&& &&>(t)); } void  IamForwording (X&& t)   {    IrunCodeActually (static_cast <X&&>(t)); } template <typename  T>void  IamForwording (T&& t)   {    IrunCodeActually (forward(t)); } 
 
列表初始化 只要#include了头文件,并且声明了一个以initialize_list模板类为参数的构造函数,就可以使得自定义的类可以使用列表初始化。  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include  <vector>  #include  <string>  using  namespace  std;enum  Gender  { boy, girl };class  People  {public :    People (initializer_list<pair<string, Gender>> l) {         auto  i = l.begin ();         for  (; i != l.end (); i++) {             data.push_back (*i);         }     } private :    vector<pair<string, Gender>> data; }; People ship2012 = {{"Garfield" , boy}, {"HelloKitty" , girl}}; 
 
列表初始化是唯一一种可以防止类型收窄的初始化方式。
1 2 3 float  f{7 }; int  g{2.0f }; A obj2{};    
 
POD类型的好处 
字节赋值,可以使用memset和memcpy对POD类型进行初始化和拷贝操作。 
提供对C内存布局兼容。 
保证了静态初始化的安全有效,比如放入目标文件的.bss段,在初始化中直接被赋为0。 
 
易用易学 auto 1 2 3 4 5 6 7 8 9 float  radius = 1.7e10 ;PI pi; auto  circumference = 2  * (pi * radius); #define  Max1(a, b) ((a) > (b) ? (a) : (b)) #define  Max2(a, b) ({ \         auto _a = (a); \         auto _b = (b); \         (_a > _b ? _a : _b; )})  
 
如果要使得auto声明的变量是另一个变量的引用,必须使用auto &。
1 2 3 const  double  a;auto  d = a; auto  & e = a; 
 
声明为引用或指针的auto变量可以带走其对象的相同属性,包括const、volatile. auto可以用来声明多个变量类型,不过这些变量的类型必须相同,否则编译报错。
1 auto  o = 1 , &p = o, *q = &p; 
 
不能推导的情况:
auto不能为函数形参类型。 
结构体非静态成员变量类型不能是auto。 
不能声明auto数据。 
vectorv 不行。  
 
decltype 与auto相同,decltype类型推导也是在编译时进行的。 decltype一个最大的用途就是用在追踪返回类型的函数中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 template <typename  T1, typename  T2>void  Sum (T1 &t1, T2 &t2, decltype (t1 + t2) &s)   {    s = t1 + t2; } void  Sum (int  a[], int  b[], int  c[])   {     } int  main ()   {    int  a[5 ], b[5 ], c[5 ];     Sum (a, b, c);      int  d, e, f;     Sum (d, e, f);  } 
 
1 2 3 4 5 6 7 8 #include  <type_traits>  using  namespace  std;typedef  double  (*func) ()  ;int  main ()   {    result_of<func ()>::type f;  } 
 
自动追踪返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 template  <typename  T1, typename  T1>auto  Sum (T1 &t1, T2 &t2)  -> decltype (t1 + t2)   {    return  t1 + t2; } int  (*(*pf ())())() {    return  nullptr ; } auto  pf1 ()  -> auto (*) ()  -> int (*)   {    return  nullptr ; } template  <typename  T>auto  Forward (T t)  -> decltype (foo(t))   {    return  foo (t); } 
 
类型安全 强类型枚举 1 2 3 4 5 6 7 #define  Male 0 #define  Female 1 enum  { Male, Female };const  static  int  Male = 0 ;const  static  int  Femal = 1 ;