1 保证稳定性和兼容性
1.1 保持与C99兼容
C++11将对一下C99特性的支持纳入新标准:
- C99中的预定义宏
- __func__预定义标识符
- _Pragma操作符
- 不定参数宏定义以及__VA_ARGS__
- 宽窄字符串连接
1.1.1 预定义宏
宏名称 | 功能描述 |
---|---|
|
如果编译器的目标系统环境中包含完整的标准C库,那么这个宏定义为1,否则宏的值为0 |
|
C编译器通常用这个宏的值来表示编译器的实现是否和C标准一致。C++11标准中这个宏是否定义以及定成什么值由编译器来决定 |
|
C编译器通常用这个宏来表示所支持的C标准版本,如1999mmL。C++11标准中这个宏是否定义以及定成什么值由编译器来决定 |
|
这个宏通常定义为一个yyyymmL格式的整数常量,如199712L,用来表示C++编译环境符合某个版本的ISO/IEC 10646标准 |
1.1.2 __func__预定义标识符
返回所在函数的名字。
编译器的实现:
const char* hello() {
static const char* __func__= "hello"; //编译器会隐式的在函数定义之后定义__func__标识符,所以__func__不能用在函数参数上
return __func__;
}
//C++11中允许__func__使用在类或者结构体
struct TestStruct {
TestStruct(): name(__func__) {}
};
1.1.3 _Pragma操作符
//与#pragma once 类似,该头文件只被编译一次
_Pragma("once")
注意:#pragma是预指令,_Pragma是一个操作符,因此_Pragma可以用在一些宏中。而#pragma不能在宏中展开。
1.1.4 变长参数的宏定义以及__VA_ARGS__
变长参数的宏定义:在宏定义中参数列表的最后一个参数为省略号,预定义宏__VA_ARGS__可以在宏定义的实现部分替换省略号所代表的字符串,如:
#define PR(...) printf(__VA_ARGS__)
1.2 宏__cplusplus
c++11中__cplusplus被预定义为201103L。
#if __cplusplus < 201103L
#error "should use C++11 implementation" //不支持C++11的代码编译立即报错并终止编译
#endif
1.3 静态断言
assert宏只在程序运行时才能起作用,#error只在编译预处理时才起作用,static_assert是在编译期起作用。
可能的实现:
#define assert_static(e) \
do { \
enum {assert_static__ = 1/(e)}; \
} while(0)
1.4 noexcept修饰符与noexcept操作符
noexcept修饰符有两种形式:
- 一种就是简单地在函数声明后加noexcept关键字
- 另一种可以接受一个常量表达式作为参数
void excpt_func() noexcept;
void excpt_func() noexcept(常量表达式);true不会抛异常,false会抛异常
noexcept作为操作符时,通常可以用于模版,如
//这里第二个noexcept就是操作符,当其参数时一个有可能抛出异常的表达式时,返回false,反之返回true。
template <class T>
void func() noexcept(noexcept(T())) {}
1.5 快速初始化成员变量
- C++98只能对常量的静态成员,且只能是整型或枚举类才能就地初始化。非静态成员变量的初始化必须在构造函数中进行。
- C++11允许非静态成员变量的初始化有多种形式,如,初始化列表、=号以及花括号{}
- 对于非常量的静态成员变量,C++11与C++98保持了一致,程序员需要用到cpp定义它,这会保证编译时,静态成员的定义最后只存在于一个目标文件中。
- 对于静态常量成员,除了const关键字外,还可以使用constexpr对静态常量成员进行声明
注意:
- ()不能对于非静态成员进行就地初始化。
- 初始化列表的效果优于就地初始化
1.6 非静态成员的sizeof
在C++98中,对于非静态成员变量的使用sizeof是不能够通过编译的,而C++11可以。
//C++98在没有定义实例的时候,获取类成员的大小
sizeof(((People*)0)->hand);
1.7 扩展的friend语法
class LiLei {
friend class Poly; //C++98
friend Poly; C++11,不需要使用class
};
//少了class,可以为类模版声明友元了
class P;
template <typename T>
class People {
friend T;
};
People<P> pp; //类型p是People的友元
People<int> pi; //对于int类型的模版参数,友元声明被忽略
1.8 final/override控制
final:使派生类不可覆盖它所修饰的虚函数
override: 该函数必须重载其基类中的同名函数。
1.9 函数模版的默认模板参数
C++98在模版类声明的时候,标准允许其有默认模板参数,C++11中允许了函数模板的模板参数。
template <typename T = int>
void DefTempParm() {};
函数模板中的默认参数无需遵守“从右往左”的规则
template <typename T1 = int, typename T2>
void DefFunc1(T1 a, T2 b);
1.10 外部模版
在头文件test.h里有声明了如下模版函数:
template <typename T> void func(T) {}
在test1.cppl里,定义:
#include "test.h"
void test1() {func(3);} //编译器实例化了函数 fun<int>(int)
在test2.cpp里定义:
#include "test.h"
void test2() {func(4);} //编译器实例化了函数 fun<int>(int)
注意:
- test1.cpp和test2.cpp 实例化了同样的函数实例,那么test1.o和test2.o的目标文件就会有两份一模一样的代码。链接器会将重复的模板函数代码删除掉,只保留一份。
- 对于数据重复,编译器无法分辨是否要共享数据,如 int a,需要显示声明为extern int a;
template void func<int>(int); //显示实例化
extern template void fun<int>(int); //外部模版的声明
1.11 局部和匿名类型作为模板参数
C++98中,局部的类型和匿名的类型不能作为模板类的实参,而C++11支持
struct A{} a;
struct {int i;} b; //b是匿名类型变量
typedef struct {int i;} B; //B是匿名类型
template <typename T>
class X{};
X<A> x1;
X<B> x2;
X<C> x3;
//匿名函数声明不能在模板实参位置
X<struct {int a;} > t; //编译不过
版权声明:本文为kaydxh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。