深入理解C++11 阅读笔记 (二)保证稳定性和兼容性

文章目录

      • 保持与C99兼容
        • C99中的预定义宏
        • __func__预定义标识符
        • _Pragma操作符
        • 变长参数的宏定义以及```__VA__ARGS__```
        • 宽窄字符串连接
      • long long 整型
      • 扩展的整型
      • 宏__cplusplus
      • 静态断言
        • 断言:运行时与预处理时
        • 静态断言与static_assert
      • noexcept修饰符与noexcept操作符
        • 修饰符用法
        • 操作符用法
      • 快速初始化成员变量
      • 非静态成员的sizeof
      • 扩展的friend语法
      • final/override控制
        • final关键字
        • 虚函数描述符override
      • 模板函数的默认模板参数
      • 外部模板
        • 为什么需要外部模板
        • 显示的实例化与外部模板的声明
      • 局部和匿名类型作模板参数

保持与C99兼容

C99中的预定义宏

预定义宏对于多目标平台代码的编写具有重大意义。通过使用#ifdef/#endif等预处理指令,可以实现一套代码完成多平台支持。

__func__预定义标识符

返回所在函数的名字。

_Pragma操作符

可以使用在宏中的#pragma

#define CONCAT(x) PRAGMA(concat on #x)
#define PRAGMA(x) _Pragma(#x)
CONCAT(..\concat.dir)
out:_Pragma(concat on "..\concat.dir")
变长参数的宏定义以及__VA__ARGS__

变长参数的宏定义指在宏定义中参数列表的最后一个参数为省略号。

预定义宏__VA_ARGS__可以在宏定义的实现部分替换省略号所代表的字符串

#define PR(...) printf(__VA_ARGS__)
#define LOG(...) { \
	fprintf(stderr,"%s: Line %d:\t", __FILE__, __LINE__);\
	fprintf(stderr,__VA_ARGS__);\
	fprintf(stderr,"\n");\
}
宽窄字符串连接

long long 整型

扩展的整型

  • signed char
  • short int
  • int
  • long int
  • long long int

宏__cplusplus

C与C++混用头文件的典型做法。

#ifdef __cplusplus
extern "C" {
#endif 
#ifdef __cplusplus
}
#endif

判断编译器是否支持C++11

#if __cplusplus < 201103L
	#error "should use C++11 implumentation"
#endif

静态断言

断言:运行时与预处理时

包括使用assert以及#if和#error配合两种方式。

#ifdef _COMPLEX_H
#error "Never use  directly;include  instead."
#endif
静态断言与static_assert

编译时执行的断言。

典型的实现:Boost内置的BOOST_STATIC_ASSERT断言机制(利用sizeof操作符)。利用“除0”会导致编译器报错这个特性来实现静态断言。

#define assert_static(e) \
	do{	\
		enum { assert_static__ = 1/(e) }; \
	} while (0)

为了解决诊断信息不充分的问题,引入了static_assert断言。

int a = 0;
std::string b = "1";
static_assert(sizeof(a) == sizeof(b), "the type of a and should same");

noexcept修饰符与noexcept操作符

修饰符用法

noexcept表示其修饰的函数不会抛出异常。如果抛出了异常,则会调用std::terminate()终止程序运行。

void except_func() noexcept;

//常量表达式的结果会被转换成一个bool类型的值。如果为真则不会抛出异常,反之则可能抛出。
void except_func() noexcept (常量表达式);
操作符用法

通常可用于模板。

template 
	void fun() noexcept(noexcept(T())) {}

第二个noexcept操作符的参数为一个可能抛出异常的表达式的时候,其返回值时false,反之为true。

快速初始化成员变量

标准允许使用等号=或者花括号{}进行就地的非静态成员变量初始化。

struct init{ int a = 1; double b {1.2}; }

在类的声明中对非静态成员进行就地列表初始化可以降低程序员的工作量。(创建多个构造函数时)

class Test{
public:
	Test(){};
    Test(int a):m_number(a){};
    Test(string str):m_str(str){};
    Test(int a,string str):m_number(a),m_str(str){};
private:
	int m_number = 1;
	string m_str = "init";
};

非静态成员的sizeof

对非静态成员变量使用sizeof操作合法。不需要实例化。

扩展的friend语法

声明一个类为另一个类的友元时,不再需要class关键字,同时支持别名的方式。

通过类模板声明友元。

class P;

template  class People {
 friend T;
};

People

PP; People Pi;

final/override控制

final关键字

用来阻止继续重写。使派生类不可覆盖它所修饰的虚函数。

struct Object{
	virtual void fun() = 0;
};
struct Base : public Object{
	void fun() final;
};
struct Derived: public Base{
	void fun(); //无法通过编译
};
虚函数描述符override

如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则无法通过编译。

模板函数的默认模板参数

无法从函数实参中推导出类型时,默认模板参数会被使用。

template 
void f(T t = 0, U u = 0);

void g(){
	f(1,'c');// 1,'c' 
	f(1);	 // 1,0   
	f();	 // 错误 T无法被推导
	f();//0,0	  
	f();//0,0 
}

外部模板

为什么需要外部模板

使用了相同模板函数且参数类型一致时,编译器会在目标文件中生成两份一摸一样的函数代码。而为了节约空间,链接器在链接的时候会通过编译器辅助的手段将重复的模板函数删掉,只保留单个副本。

多处的模板实例化都需要编译器去做实例化的工作,而在链接的时候,还需要移出重复的实例化代码。导致编译器产生大量的冗余代码,增加编译时间和链接时间。使用“外部的”模板可以解决这个问题。

显示的实例化与外部模板的声明
template  void fun(T) {}

#include "test.h"
template void fun(int);	//显示的实例化
void test1() { fun(3); }

#include "test.h"
extern template void fun(int);	//外部模板的声明
void test1() { fun(3); }

外部模板不能用于一个静态函数(即文件域函数),但可以用于类静态函数(静态函数没有外部链接属性)。

局部和匿名类型作模板参数

你可能感兴趣的:(C++学习,C++11阅读笔记)