《深入浅出C++11之1保证稳定性和兼容性》

1 保证稳定性和兼容性

1.1 保持与C99兼容

C++11将对一下C99特性的支持纳入新标准:

  • C99中的预定义宏
  • __func__预定义标识符
  • _Pragma操作符
  • 不定参数宏定义以及__VA_ARGS__
  • 宽窄字符串连接

1.1.1 预定义宏

宏名称 功能描述
__STDC_HOSTED__ 如果编译器的目标系统环境中包含完整的标准C库,那么这个宏定义为1,否则宏的值为0
__STDC__ C编译器通常用这个宏的值来表示编译器的实现是否和C标准一致。C++11标准中这个宏是否定义以及定成什么值由编译器来决定
__STDC_VERSION__ C编译器通常用这个宏来表示所支持的C标准版本,如1999mmL。C++11标准中这个宏是否定义以及定成什么值由编译器来决定
__STDC_ISO_10646__ 这个宏通常定义为一个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 快速初始化成员变量

  1. C++98只能对常量的静态成员,且只能是整型或枚举类才能就地初始化。非静态成员变量的初始化必须在构造函数中进行。
  2. C++11允许非静态成员变量的初始化有多种形式,如,初始化列表、=号以及花括号{}
  3. 对于非常量的静态成员变量,C++11与C++98保持了一致,程序员需要用到cpp定义它,这回保证编译时,静态成员的定义最后只存在于一个目标文件中。
  4. 对于静态常量成员,除了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; //编译不过

猜你喜欢

转载自blog.csdn.net/kaydxh/article/details/106302814