二 保证稳定性和兼容性
保持与C99兼容
预定义宏
C99语言标准增加的一些预定义宏,C++11同样增加了对这些宏的支持
- __func__预定义标识符
功能是返回所在函数的名字,在C++11中,标准甚至允许其使用在类或者结构体中
- _Pragma操作符
C/C++标准中,#pragma是一条预处理的指令,用来向编译器传达语言标准以外的一些信息,例如#pragma once 表示该头文件应该只被编译一次
在C++11中,标准定义了与预处理指令#pragma功能相同的操作符_Pragma,例如 _Pragma ("once");
- 变长参数的宏定义以及__VA_ARGS__
在C99标准中,变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS__则可以在宏定义的实现部分替换省略号所代表的字符串
#define PR(...) printf(__VA_ARGS__)
- 宽窄字符串的连接
C++标准中,将窄字符串(char)转换成宽字符串(wchar_t)是未定义的行为
在C++11标准中,在将窄字符串和宽字符串进行连接时,会将窄字符串转换成宽字符串,然后再与宽字符串进行连接
宏__cpluscplus__
C与C++混用头文件的典型用法
1 #ifidef __cplusplus 2 extern "C" { 3 #endif 4 // 一些代码 5 #ifdef __cplusplus 6 } 7 #endif
C++11中 __cplusplus 被定义为 201103L 可以用来检测是否支持C++11
#if __cplusplus < 201103L #error "should use C++11 implemention " //预编译指令 #endif
静态断言
断言就是将一个返回值总是需要为真的判别式放在语句中,用于排除在设计的逻辑上不应该产生的情况,以迫使程序在发生异常时,退出,从而避免程序陷入逻辑的混乱
头文件assert.h定义的宏受NDEBUG的影响.如果预程序在处理这个头文件时已经定义了NDEBUG,assert宏的内容就定义为空,这意味着assert宏不在起作用.所以,可以在最终
发布程序的时候可以使用-DNDEBUG关闭断言功能或者把#define NDEBUG加到每个源文件中,但这条语句必须放在#include <assert.h>之前.
- 静态断言与static_assert
assert运行时断言
C++11标准中,引入了static_assert断言,解决编译时断言
static_assert(常量表达式,提示字符串)。
如果第一个参数常量表达式的值为真(true或者非零值),那么static_assert不做任何事情,就像它不存在一样,否则会产生一条编译错误,错误位置就是该static_assert语句所在行,错误提示就是第二个参数提示字符串。
noexcept修饰符与noexcept操作符
C++98中,动态异常声明throw(int,double), C++11中被弃用
1 void except_func() throw(int, double){ ... }
表示函数不会抛出异常的动态异常声明throw()也被新的noexcept异常声明所取代
- noexcept
void except_func() noexcept; 表示不抛出异常
void except_func() noexcept(常量表达式);表达式为true不会抛出异常;flase抛出异常
快速初始化成员变量
在C++98中,支持了在类声明中使用等号“=”加初始值的方式,来初始化类中静态成员常量。这种方式我们也称之为”就地“声明。
C++11标准允许,对非静态成员变量初始化有多种形式
- C++11中允许使用等号=或者花括号{}进行就地的非静态成员变量初始化
- C++11中,保留了初始化列表
非静态成员的sizeof
c++11中,允许对非静态成员变量使用sizeof,而 C++98 标准只允许静态成员变量,或者对象的实例才能对其成员进行sizeof操作
1 struct People { 2 public: 3 int hand; 4 static People * all; 5 }; 6 7 int main() 8 { 9 People p; 10 cout << sizeof(p.hand) << endl; // C++98中通过, C++11中通过 11 cout << sizeof(People::all) << endl; // C++98中通过, C++11中通过 12 cout << sizeof(People::hand) << endl; // C++98中错误, C++11中通过 13 }
扩展的friend语法
- C++11中,声明一个类为另外一个类的友元时,不再需要使用class关键字
- c++11中, 别名同样可行
- C++11中, 可以为类模板声明友元
final/override控制
- final 派生类不可覆盖它所修饰的虚函数
- override 必须重写其基类中的同名函数,否则代码将无法通过编译
模板函数的默认模板参数
外部模板
局部和匿名类型作模板实参
三 通用为本,专用为末
继承构造函数
隐藏:类具有可派生性,派生类可以自动获得基类的成员变量和接口(虚函数和纯虚函数,这里我们指的都是public派生)。不过基类的非虚函数则无法再被派生类使用了。
这条规则对于类中最为特别的构造函数也不例外,如果派生类要使用基类的构造函数,通常需要在构造函数中显式声明
- C++中,使用using声明,实现构造函数透传到派生类
1 struct Base{ 2 void f(double i) {} 3 }; 4 struct Derived : Base{ 5 using Base::f; 6 void f(int i){} 7 }; 8 9 int main(){ 10 Base b; 11 b.f(4.5); // Base:4.5 12 13 Derived d; 14 d.f(4.5); // Base:4.5 15 }
注:
如果积累的构造函数被声明为私有成员函数,或者派生类是从基类中虚继承来的,那么就不能再派生类中声明继承构造函数,
如果使用继承构造函数,编译器就不会载为派生类生成默认构造函数
委派构造函数
1 class Info { 2 public: 3 Info() : Info(1) {} // 委派构造函数 4 Info(int i) : Info(i, 'a') {} // 既是目标构造函数,也是委托构造函数,是一种链状委托构造 5 Info(char e) : Info(1, e) {} 6 private: 7 Info(int i, char e) : type(i), name (e) {} // 目标构造函数 8 int type; 9 char name;
注:
1、C++中,构造函数不能同时委派和初始化成员,如果委派构造函数要为成员变量赋值,初始化代码必须放在函数体内
2、构造函数比较多时可能拥有不止一个委派构造函数,而目标构造函数很可能也是委派构造函数,这样一来可能在委派构造函数中形成链状的委派构造关系
有一点要注意不能形成委托环