《深入理解C++11:C++11新特性解析与应用》笔记二

 

第二章 保持稳定性和兼容性

2.1 保持与C99兼容

2.1.1 预定义宏

c99语言标准增加一些预定义宏,c++11同样增加了对这些宏的支持。

2.1.2 __func__预定义宏

返回所在函数的名字,不能用于参数声明。

2.1.3 _Pragma操作符

在c/c++标准中,#pragma用来向编译器传达语言标准以外的一些信息。#pragma once指示编译器该头文件只被编译一次。c++11定义了与#pragma相同功能的操作符_Pragma,操作符的好处是可以用在一些宏中,而#pragma不能在宏中展开。

2.1.4 变长参数的宏定义以及__VA_ARGS__

c99标准中变长参数是指在宏定义中参数列表的最后一个参数为省略号,预定义宏__VA_ARGS_可以在宏定义的实现部分替换省略号所代表的字符串。

2.1.5 宽窄字符串的连接

c++11标准中窄字符串char和宽字符串wchar_t进行连接时,会先将窄字符串转换为宽字符串,然后再与宽字符串进行连接。

2.2 long long整型

c++11标准要求long long整型可以在不同平台有不同的长度,但至少有64位。可以使用LL或ll后缀标识一个long long整型的字面量,用ULL或ull,Ull,uLL表示一个unsigned long long类型的字面量。要了解平台上long long大小的方法,可以查看<climits>或<limits.h>中的宏LLONG_MIN、LLONG_MAX和ULLONG_MIN。

2.3 扩展的整型

c++11一共定义了5种标准的有符号整型:

signed char,short int,int,long int,long long int

c++11规定编译器可以扩展整型,有符号和无符号类型占用同样大小的内存空间。当运算、传参等类型不匹配的时候,整型间会发生隐式的转换,这个过程称为整型的提升。规则:1.长度越高等级越高。2长度相同时,标准整型等级高于扩展类型。3相同大小的有符号和无符号类型等级相同。

2.4 宏__cplusplus

__cplusplus宏通常被定义为一个整型值。

2.5 静态断言

2.5.1 断言:运行时与预处理时

c++中,标准在<cassert>或<assert.h>头文件中提供assert宏,用于在运行时进行断言。

通过预处理指令#if和#error配合,也可以在预处理阶段进行断言。例如:

2.5.2 静态断言与static_assert

断言assert宏只有在程序运行时才能起作用。有时候我们希望在编译时能做一些断言。c++11标准引入static_assert断言。static_assert要求断言表达式结果必须是在编译时期可以计算的表达式,即必须是常量表达式。

2.6 noexcept修饰符与noexcept操作符

以前我们通过在函数声明后定义一个动态异常声明,类似throw(int,double),指出函数可能抛出的异常类型。c++11中弃用了这个特性。表示函数不会抛出异常的动态异常声明throw()也被新的noexcept异常声明所取代。

noexcept表示其修饰的函数不会抛出异常。如果抛出异常,编译器可以选择直接调用std::terminate()函数来终止程序的运行。这比基于异常机制的 throw()在效率上会高一些。这是因为异常机制会带来一些额外开销,比如函数抛出异常,会导致函数栈被依次地展开(unwind ),并依帧调用在本帧中已构造的自动变量的析构函数等。

noexcept修饰符可以选择接受一个常量表达式作为参数。表达式值为true,表示函数不会抛出异常,反之则有可能抛出异常。不带表达式的时候相当于声明了noexcept(true)。对于可能抛出异常的函数,使用noexcept(false)修饰。

noexcept可以阻止异常的传播与扩散。

noexcept作为一个操作符时,可以用于模板:

这样就可以使模板函数根据条件实现noexcept修饰版本或无noexcept修饰的版本。

出于安全考虑,c++标准让类的析构函数默认是noexcept(true)的,常被析构函数调用的delete函数也默认是noexcept的。

2.8 非静态成员的sizeof

c++98标准中,对非静态成员变量使用sizeof是不能通过编译的,只允许对静态成员或者对象实例的成员进行sizeof操作。c++11中sizeof可以作用于类成员表达式:

2.9 扩展的friend语法

c++11对friend进行了改造。声明一个类为另一个类的友元时,不再需要使用class关键字。这样就可以为类模板声明友元了,这在c++98中无法做到:

 

2.10 final/override控制

c++11引入final关键字,用于在派生过程中任意地阻止一个接口的可重载性。

c++中的重载还有一个特性,对于基类声明为virtual的函数,之后派生类的重载版本都不需要再声明该重载函数为virtual。这样带来两个问题,一是无法看出一个函数是否是虚函数,二是虚函数跨层,也就是没有在父类中声明的接口,有可能是祖先的虚函数接口。c++11引入虚函数描述符override。如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,如果基类中不存在同名虚函数,或者参数不一致等,编译就会失败:

2.11 模板函数的默认模板参数

c++98引入了函数模板和类模板,模板类在声明的时候,标准允许其有默认模板参数,但是不支持函数模板的默认模板参数,c++11中解除了这一限制:

在为类模板多个模板参数指定默认值时,需要按照从右往左的规则进行指定,函数模板并没有这个限制,例如:

如果能从函数实参推导出类型,则函数模板的默认参数不会被使用,反之,默认模板参数会被使用。

2.12 外部模板

2.12.1 为什么需要外部模板

在多个源代码中出现的每一处模板实例化,编译器都要去做实例化工作;在链接时,编译器还需要移除重复的实例化代码。这样的工作过于冗余,而且在广泛使用模板的项目中,编译会产生大量冗余代码,会极大地增加编译时间和链接时间。使用外部模板可以解决这个问题。

2.12.2 显式的实例化与外部模板的声明

显式实例化语法:

对于模板:template <typename T> void fun(T){},只需要声明template void fun<int>(int),就可以使编译器在本编译单元实例化出一个fun<int>(int)版本的函数。外部模板的声明就是在显式实例化基础上加上关键字extern:extern template void fun<int>(int)。外部模板声明不能用于一个静态函数,但可以用于类静态类成员函数。因为静态函数没有外部链接属性,不可能在本编译单元之外出现。

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

c++98标准对模板实参的类型有限制,局部类型、匿名类型在c++98中不能作为模板类的实参,例如:

c++11中放开了这些限制,但是仍然不允许把匿名的结构体直接声明在模板实参的位置:

参考资料:

1.《深入理解C++11:C++11新特性解析与应用》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值