《深入理解C++11》阅读笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeffdeen/article/details/53293242

保证稳定性和兼容性

  • __FUNCTION__ 返回函数名或者结构名

  • __Pragma__ 操作符 __Pragma(“once”) 防止重复编译 与 #ifndef 效果一样

  • __VA__ARGS__ 这个可以在宏定义的实现部分替换省略号

    #define PR(...) printf(__VA__ARGS__)

  • long long 整型

  • 宏__cpluplus

  • 静态断言 static_assert 编译时期进行处理

  • noexcept修饰符

  • 快速初始化成员变量

  • sizeof 可以直接用类加作用域操作符然后获得非静态成员变量大小

    sizeof(People::hand);
  • 扩展的firend用法 在C++11中声明一个类为另外一个类的友元时,不再需要class关键字

    class Poly;
    class Lilei{
       friend Poly;
    };
  • final/override 控制

    • final 作用是禁止子类重载基类的某个virtual方法,或者禁止该类被继承

    那么基类中的虚函数是否可以使用final关键字呢?答案是肯定的,不过这样使用该虚函数将无法被重载,也就失去了虚函数的意义。…..final通常只在继承关系的“中途”终结派生类的重载中有意义。

    • override 防止在重载基类函数时,意外重写基类函数或者函数名拼写错误
  • 模板函数的默认模板参数 C++11支持类模板和模板函数的默认参数

  • 外部模板 避免实例化多份模板实例

    template void fun<int> (int)
    extern template void fun<int> (int)`
  • 局部和匿名类型作模板实参


通用为本,专用为末

  • 继承构造函数

  • 委派构造函数

  • 右值引用

    • 右值引用不能绑定任何左值

      int c;
      int && d = c;     //error
    • 常亮左值引用可以接受非常量左值引用、常量左值引用、右值对其初始化

    • 右值引用本身是个左值

    • 移动语义(move)一定是要修改临时变量的值,所以不能加上const

    • 完美转发(forward)是指在函数模板中,完全按照模板的参数类型,将参数传递给模板中调用的另外一个函数。即如果相应实参是左值,它就应该被转发为左值;同样如果相应实参是右值,它就应该被转发为右值。

  • 显式转换操作符 C++11中explicit可以加到自定义的类型转换操作符上

  • 列表初始化

    • 自定义类型和标准程序库中的vector之类的均可以使用列表初始化
     vector<int> c{1,3,5};
  • 使用initialize < T > 模板类为参数的构造函数,也可以使得自定义类型可以使用列表初始化

  • 在C++11中使用列表初始化可以防止类型收窄

    • POD类型 POD类型是平凡的和标准布局的
  • 平凡的

    • 拥有平凡的默认构造函数和析构函数
    • 拥有平凡的拷贝构造函数和移动构造函数
    • 拥有平凡的拷贝赋值运算符和移动赋值运算符
    • 不能包含虚函数以及虚基类
  • 标准布局

    • 所有非静态成员具有相同的访问权限
    • 在类或者结构体继承时,满足

      • 派生类中有非静态成员,且只有一个包含静态成员的基类

      • 基类有非静态成员,而派生类没有非静态成员

    • 类中第一个非静态成员的类型与其基类不同
    • 没有虚函数和虚基类
    • 所有非静态数据成员均符合标准布局类型,其基类也符合
  • 使用POD可以安全地字节赋值,并且与C兼容,保证静态初始化的安全有效

    • 非受限联合体
  • C++11允许union中的成员为POD类型

  • C++11允许静态成员函数存在于union中

    • 用户自定义字面量

      struct Watt { unsigned int v; };
      Watt operator "" _W(unsigned long long v) {
        return{ (unsigned int)v };
      }
      int main() {
        Watt capacity = 1024_W;
        std::cout << capacity.v;
      }
    • 内联名字空间 内联的名字空间允许程序员在父名字空间定义或特化子名字空间的模板

    • 模板的别名

    template<typename T>
    using MapString = std::map<T, int>;
    
    int main() {
    u_Test<int> t1;
    MapString<int> MAP;
    }

新手易学,老兵易用

  • ​ 右尖括号的改进

    const vector<int> v = static_cast<vector<int>> (V);
  • auto类型推导

    • 优点:

    • 使用auto避免复杂类型的变量声明

    • 免除程序员在一些类型声明时的麻烦

    • 自适应能够在一定程度上支持泛型

    • 使用准则:
    • auto同时声明多个变量时,只有第一个被用于类型推导,所以这些类型必须相同
    • auto不能作为形参类型

    • 对于结构体来说,非静态成员变量的类型不能是auto

    • 直接声明auto数组会出错

  • decltype decltype的类型推导并不像是auto一样是从变量声明的初始化表达式获得变量的类型,而是以一个普通的表达式为参数,返回该表达式的类型。

    • 推导四原则

    • 如果e是一个没有带括号的标记符表达式或者类成员访问表达式,那么decltype(e)就是e所命名实体类型

    • 否则,假设e的类型是T,如果e是一个将亡值,那么decltype为T&&

    • 否则,假设e的类型是T,如果e是一个左值,那么decltype(e)为T&

    • 否则,则为e

    • decltype可以带走“cv”限制符
  • 追踪返回类型

    template<typename T1, typename T2>
    auto Sum(T1& t1, T2& t2) -> decltype(t1+t2){
     return t1+t2;
    }
  • 基于范围的for循环

    int arr[5] = {1,2,3,4,5};
    for(int & e : arr){
     cout<<e<<endl;
    }

数组的大小必须确定

提高类型安全

  • 强类型枚举

    • 传统的enum的成员名字都是全局可见的,这样会导致一些成员被污染

    • 枚举的成员总是可以被隐式地转换为整型

    • C++11 强类型枚举

      enum class Type{General, Light, Medium, Heavy};
    • 优点:

    • 强作用域:强类型枚举成员的名称不会被输出到其父作用域空间

    • 转换限制:强类型枚举成员的值不可以与整型隐式地相互转换

    • 可以指定底层类型:默认为int,但是也可以指定其他除wchar_t以外的任何整型

      enum class Type: char{General, Light, Medium, Heavy};
  • 堆内存管理:智能指针与垃圾回收

    • unique_ptr:与所指对象绑定紧密,不能与其他unique_ptr之战共享,只能通过move来转移

    • share_ptr:允许多个该智能指针共享同一份内存,实现上采用了引用计数

    • weak_ptr:可以指向share_ptr指针指向的内存,但是并不拥有它,引用计数不增加,调用lock时,可以返回一个share_ptr对象,所指内存无效时,返回空指针。

    • 垃圾回收分类

    • 基于引用计数
    • 基于跟踪处理

      • 标记 - 清除
      • 标记 - 整理
      • 标记 - 拷贝
    • C++ 垃圾回收

    编译器基本不支持

提高性能及操作硬件的能力

  • 常量表达式

    • const:运行时常量性,constexpr:编译时常量性

      cosntexpr int GetConst(){ return 1;}
    • 常量表达式函数

    • 函数体只有单一的return语句

    • 必须有返回值

    • 在使用前已有定义

    • 返回的必须是一个常量表达式

  • 变长模板

    template<typename... Elements> class tuple;
    tuple<int, char, double>
  • 原子类型与原子操作

    atomic_llong total{0};
    void func(int){
     for(long long i = 0; i < 100000000LL; ++i){
       total+=i;
     }
    }
    int main(){
     thread t1(func,0);
     thread t2(func,0);
     t1.join();
     t2.join();
    }
    • atomic类模板 通过该模板可以定义任意的原子类型

      std::atomic<T>  t;
    • 原子类型只能从其模板参数类型中构造,不允许原子类型进行拷贝构造,移动构造以及使用operator=

      atomic<float> af{1.2f};
      atomic<float> af1{af};  //无法通过编译
      float f = af;  //可以运行
    • 默认情况下,C++11中的原子类型的变量在线程中总是保持着顺序执行的特性,这叫“顺序一致性”

    • 可以通过memory_order_XXX来指定原子操作的执行顺序

      atomic<int> a{0};
      atomic<int> b{0};
      int ValueSet(int){
      int t=1;
      a.store(t,memory_order_relaxed);
      b.store(2,memory_order_relaxed);
      }
  • 线程局部存储

    int thread_local errCode;

​ 每个线程将拥有独立的errCode的拷贝,线程之间相互不影响。

  • 快速退出

    • quick_exit与at_quick_exit 不执行析构函数直接退出

为改变思考方式而改变

  • 指针空值 – nullptr

  • 默认函数的控制

    • “=default”

      class Two{
      public:
      Two() = default;
      Two(int i) : data(i){}
      private:
      int data;
      }

      default关键字显示指定编译器生成该函数的默认版本

    • “=delete”

      Two(const Two& t) = delete;

      delete阻止编译器生成该函数的缺省版本,用户也无法直接

  • lambda函数

    • [capture](parameters) mutable -> return_type{statement}

      int boys=4,int girls=3;
      auto totalChild=[girls,&boys]() -> int{ return girls+boys;};
      return totalChild();
    • [var]表示按值传递

    • [=]表示值传递方式捕捉所有父作用域的变量

    • [&var]表示引用传递捕捉变量var

    • [&]表示引用传递捕捉所有父作用域的变量

    • [this]表示值传递方式捕捉当前的this指针

    • [=,&a,&b]表示按照引用传递变量a和b,值传递方式捕捉其他变量

    • 在现阶段,通常编译器都会把lambda函数转化成为一个仿函数对象

    • 对于按值传递的捕捉列表,其传递的值在lambda函数定义的时候就已经决定好了,而按照引用传递的方式,其传递的值等于lambda函数调用时候的值。

融入实际应用

  • 对齐支持

    • alignof 查看数据的对齐方式

    • alignas 设定数据的对齐方式

  • 通用属性

    • [[noreturn]] 用于标识那些不会将控制流返还给原调用函数的函数

    • [[carries_dependency]] 主要为了解决弱内存模型平台上使用memory_order_consume内存顺序枚举问题

  • Unicode支持

    • C++98定义的wchar_t在不同的编译器上的位宽不一致,导致代码不可移植
  • char16_t 用于存储UTF-16编码的Unicode数据

  • char32_t 用于存储UTF-32编码的Unicode数据

    • C++11还定义一些常量字符串的前缀
      • u8 表示UTF-8编码
      • u 表示为UTF-16编码
      • U 表示UTF-32编码
  • 原生字符串

    • 字符串前面加入R,并在引号中使用括号标识

      cout<<R"(hello\n)";   //输出hello\n
展开阅读全文

没有更多推荐了,返回首页