《深入理解C++11》笔记--扩展

上一篇:《深入理解C++11》笔记–兼容C99
本篇介绍第二章中间部分的内容。
这部分内容主要是对原有的一些关键字、类型、宏等进行了扩展。

  • long long整型
  • 扩展的整形
  • 宏__cplusplus
  • 静态断言
  • noexcept修饰符、noexcept操作符
  • 快速初始化成员变量
  • 非静态成员的sizeof
  • 扩展的friend语法
  • final/override控制

long long整型
long long整形包含long long和unsigned long long,在不同的平台上可能有不同长度,但是至少有64位。在写常数字面量时,可以使用ll或LL、ull或ULL后缀来表示long long。例如:

long long test = -10000000000LL;
unsigned long long test = 10000000000ULL;

查看不同平台的long long大小可以通过中定义的宏,long long 对应的是LLONG_MIN、LLONG_MAX、ULLONG_MAX(此处书中是MIN,应该有误)。

int main()
{
    std::cout << LLONG_MIN << std::endl;   // -9223372036854775808
    std::cout << LLONG_MAX << std::endl;   // 9223372036854775807
    std::cout << ULLONG_MAX << std::endl;  // 18446744073709551615

    return 0;
}

扩展的整形
C++11标准中一共有5种整形,char/short/int/long/long long,每种整形有signed和unsigned两种。有时候可能需要对整形进行扩展,所以C++11标准允许不同的编译器定义自己的扩展整形。标准中没有对扩展整形的命名有限制,但是必须遵循整形的规则:
1、无符号和有符号整形占用相同的内存空间
2、整形间会发生隐式转换:长度短的转换成长度长的、扩展的转换成标准的、有符号的转换成无符号的。

宏__cplusplus
C++11中的__cplusplus定义的值为201103L,而C++98/03则为199711L,可以利用这个对代码只支持C++11标准进行限制。

#ifdef __cplusplus < 201103L
#error "should use C++11 implementation"
#endif

静态断言
在之前的标准中断言assert可以起到终止程序运行的作用,例如:

char* my_malloc(int n)
{
    assert(n > 0);               // 中断退出
    return new char[n];
}

int main()
{
    char* p = my_malloc(0);

    return 0;
}

因为assert的表达式不为true,此时程序会在assert处中断。assert需要包含头文件<cassert>,想要assert不生效只需要在引用头文件之前定义NDEBUG。assert作用于运行时,在预处理时也可以进行断言,就是上面使用到的#error预处理指令。而C++11提供了编译时的断言即静态断言static_assert,接受两个参数,一个返回值为bool的表达式,一个错误信息。

char* my_malloc(int n)
{
    static_assert(n > 0, "error");   // Static_assert expression is not an integral constant expression.编译错误,表达式必须是常量
    static_assert(0 > 0, "error");   // 编译时会提示自定义的错误 Static_assert failed "error"
    return new char[n];
}

noexcept修饰符、noexcept操作符
noexcept可以用来修饰函数,在函数的后面加上noexcept,代表这个函数不会抛出异常,如果抛出异常程序就会终止。这个特性在C++11中出现很多,主要是为了当程序不该出现异常的地方抛出异常时终止程序,例如delete函数、析构函数默认都是noexcept。

void my_exception()
{
    throw 1;
}

int main()
{
    try {
        my_exception();
    } catch (...) {
        std::cout << "throw" << std::endl;  // throw
    }

    return 0;
}

上面的代码最终会输出throw,如果在函数my_exception后加上noexcept,void my_exception() noexcept,程序就会直接退出。noexcept还能加上常量表达式参数,例如noexcept(true),当常量表达式结果为true时,标识该函数不能抛出异常,否则能够抛出异常。noexcept不带参数时默认为true。
noexcept还能作为操作符noexcept(expression),noexcept 操作符不对 expression 求值。若 expression 含有至少一个下列潜在求值的构造则结果为 false :
1、调用无不抛出异常指定的任意类型函数,除非它是常量表达式。
2、throw 表达式。
3、目标类型是引用类型,且转换时需要运行时检查的 dynamic_cast 表达式
4、参数类型是多态类类型的 typeid 表达式
5、所有其他情况下结果是 true 。
关于noexcept操作符另外会有单独一篇进行说明《深入理解C++11》笔记–noexcept

快速初始化成员变量
C++11支持对非常量非静态变量进行直接初始化,而不必使用初始化列表。当然,直接初始化和初始化列表可以同时使用,但是结果以初始化列表为准。

class Test{
    static int a1 = 0;               // C++11和C++98都不支持
    const static int a2 = 0;         // C++11和C++98都支持
    int a3 = 0;                      // C++11支持
    int a4{0};                       // C++11支持
};

直接初始化更多可能用于类中有多个构造函数,避免多次写初始化列表。

非静态成员的sizeof
C++11支持非静态成员sizeof,例如:

class Test{
public:
    static int a1;
    int a2 = 0;
};

int main()
{
    Test t;
    std::cout << sizeof(t.a1)  << std::endl;      // C++11、C++98都编译通过
    std::cout << sizeof(Test::a1)  << std::endl;  // C++11、C++98都编译通过
    std::cout << sizeof(Test::a2)  << std::endl;  // C++11编译通过

    return 0;
}

扩展的friend语法
C++11扩展了friend语法:
1、可以不使用class
2、可以使用别名

class Number;
typedef Number Num;

class One{
    friend class Number;   // C++11、C++98都允许
};

class Two{
    friend Number;         // C++11允许
};

class Three{
    friend Num;            // C++11允许
};

由于上述变化,C++11中还能为类模板设置友元类,例如:

template<typename T> class Test{
    friend T;
};

final/override控制
final修饰符能够用来终止虚函数的重写。

class Number{
    virtual void get(){}
};

class One : public Number{
    void get() final {}    // final修饰后,子类无法再重写
};

class OneFloat : public One{
    void get() {}          // 编译失败
};

我们在重写父类的虚函数时,子类不必再函数前面加上virtual修饰,这可能会有一个问题,就是子类实现重写的时候并没有完全和父类虚函数一致,从而导致重新定义了一个函数,而重写失败。在C++11中新增了一个override修饰符,可以避免这个问题,有override修饰符的函数必须在父类存在对应的虚函数,例如:

class Number{
    virtual void get(bool flag){}
};

class One : public Number{
    void get() override {}            // 编译失败
};

class Two : public Number{
    void set() override {}            // 编译失败
    void get(bool flag) override {}   // 编译成功
};

下一篇:《深入理解C++11》笔记–模板

猜你喜欢

转载自blog.csdn.net/wizardtoh/article/details/80572266