• c++11 中的注意事项


    1. C++11标准中让类的析构函数默认也是noexcept(true)的。 但如果显式地为析构函数指定了noexcept,或者类的基类或成员有noexcept(false)的析构函数,析构函数就不会再保持默认值。

    2. 初始化成员变量:

    c++98中,支持在类声明中使用等号“=”进行初始化,但要求必须为静态成员常量,而且也只能是整形或枚举类型才能初始化。

    而在c++11中,允许使用等号或花括号进行非静态成员变量的初始化,比如:

    struct init{ int a = 1; double b {1.2}; }

    上述代码中,给非静态成员a和b分别赋予初值1和1.2.

    若同时使用就地初始化和初始化列表两种方式,结果是初始化列表后作用于非静态成员,即最终所赋值的是初始化列表中的值。

    3. 扩展的friend语法:

    class Poly;
    typedef Poly P;
    
    class LiLei {
     friend class Poly; // c++98通过,c++11通过
    };
    
    class Jim {
     friend Poly;  //c++98失败,c++11通过   
    };
    
    class HanMeiMei {
     friend P;  //c++98失败,c++11通过
    };

    c++11在声明一个类为另一个类友元时,不再需要使用class关键字,或者使用别名。

    这样可以为类模板声明友元:

    class P;
    
    template <typename T> class People {
     friend T;
    };
    
    People<P> PP;  //类型P在这里是People类型的友元
    People<int> Pi; //对于int类型模板参数,友元声明被忽略

    4. c++11中阻止一个函数在派生类中被重载,使用final:

    class Object {
     virtual void fun() = 0;
    };
    
    class Base: public Object {
     void fun() final;
    };
    
    class Derived: public Base {
     void fun();  //无法通过编译
    };

    在类Base中将函数fun()声明为final,那么派生于Base的Derived类则不能重载fun()。

    若在派生类中在虚函数声明时使用override,表明该函数必须重载其基类中能的同名函数:

    class Base {
     virtual void Turing() = 0;
     virtual void Dijkstra() = 0;
     virtual void VNeumann(int g) = 0;
     void print();
    };
    
    class Derived: public Base {
     void Turing() override;
     void Dikjstra() override;  //无法通过编译,拼写错误
     void VNeumann(double g); //无法通过编译,参数不一致
     void print() override;  //无法通过编译,非虚函数重载
    };

    若没有override修饰符,则Derived则会通过编译,但并没有实现想要重载的功能。

    5. 模板函数的默认模板参数

    在c++98中函数模板不能有默认参数,而在c++11中则可以有:

    void DefParm(int m = 3) {}  //c++98通过编译,c++11通过编译
    
    template <typename T = int>
    class DefClass {};    //c++98通过编译,c++11通过编译
    
    template <typename T = int>
    void DefTempParm() {};  //c++98编译失败,c++11通过编译

    对于类模板来说,默认模板参数声明指定默认值时,需要按照从右往左的规则进行指定,而对于函数模板则不是必须的。

    template <typename T1, typename T2 = int> class DefClass1;
    template <typename T1 = int, typename T2> class DefClass2; //无法通过编译
    
    template <typename T, int i = 0> class DefClass3;
    template <int i = 0, typename T> class DefClass4; //无法通过编译
    
    template <typename T1 = int, typename = T2> void DefFunc1(T1 a, T2 b);
    template <int i = 0, typename T> void DefFunc2(T a);

    6. 外部模板

    编译时,对于源代码中出现的每一处模板实例化,编译器都需要去做实例化的通过;而在链接时,链接器还需要移除重复的实例化代码。解决该问题的方法是使用外部模板。c++98中已有的一个特性是显式实例化:

    template <typename T> void fun(T) {}
    
    声明:
    template void fun<int>(int);

    在c++11中,外部模板的声明为:

    extern template void fun<int>(int);

    7. 局部和匿名类型作为模板实参

    template <typename T>
    class X {};
    
    template <typename T>
    void TempFun(T t) {}
    
    struct A {} a;
    struct { int i; } b;  //匿名类型变量
    typedef struct { int i; } B; //匿名类型
    
    void Fun() {
     struct C {} c;  //局部类型
     
     X<A> x1;  //c++98通过,c++11通过
     X<B> x2;  //c++98不通过,c++11通过
     X<C> x3;  //c++98不通过,c++11通过
     TempFun(a);  //c++98通过,c++11通过
     TempFun(b);  //c++98不通过,c++11通过
     TempFun(c);  //c++98不通过,c++11通过
    }

     8. constexpr 近似const,可以修饰变量,也可以修饰函数

    const int global = 100;
    int temp = 100;
    constexpr int a = 1;  //right
    constexpr int b = global; //right
    constexpr int c = temp; //wrong

    constexpr修饰的函数,生效于编译时而不是运行时,重点应用于修饰函数使其在编译期大幅度被解释;被constexpr修饰的函数,无论是普通函数,还是类成员函数,必须是编译器可计算得到结果,即字面常量,不可是运行时才能读取的内容。

    9. using取代typedef

    typedef double db;
    using db = double;
    typedef void(*function)(int, int);
    using function = void(*)(int, int);

    10. emplace

    作用域容器,区别于push、insert等。push_back是在容器尾部追加一个容器类型对象,emplace_back是构造一个新对象并追加在容器尾部。

    std::vector<A> vec;
    A a(10);
    vec.push_back(a);
    
    std::vector<A> vec;
    vec.emplace_back(10);

    11. override/final: 显式的标识是否应该多态继承

    override: 子类用override修饰其虚函数,表示要多态继承基类的虚函数,不可以修饰非虚函数。

    final: 基类用final修饰其虚函数,表示其子类不可以多态继承该虚函数。

    12. 类型说明符auto

    auto用于从初始化表达式中推断出变量的数据类型。因此,auto定义的变量必须有初始值。

    13. 类型说明符decltype

    decltype的作用是选择并返回操作数的数据类型。编译器会分析表达式并得到它的类型,但是不会去计算表达式的值。如果decltype使用的是一个不加括号的变量,得到的就是该变量的类型。如果给变量加上了一层括号,编译器会把它当作一个表达式,得到的则是引用类型。

    int i = 10;
    decltype(i) a; // a的类型是int
    decltype((i)) b = i; //b的类型是int&,必须为其初始化,否则编译错误
  • 相关阅读:
    C. MP3(离散化 暴力)
    最大团、最小独立集
    欧拉函数
    In Touch(dijk+并查集优化)
    Path(2019 杭电多校第一场 ) hdu 6582(最短路模板+dinic模板)
    2019 南昌邀请赛 Winner (tarjan缩点)
    mybatis主键回填和自定义
    mybatis配置xml文件的层次结构
    Paratroopers
    Dual Core CPU
  • 原文地址:https://www.cnblogs.com/sssblog/p/10180809.html
Copyright © 2020-2023  润新知