上一篇:《深入理解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》笔记–模板