五十一、通过定义类为final来阻止继承
有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑它是否适合作为一个基类。为了实现这一目的,C++11新标准提供了一种防止继承的方法,即在类名后面跟一个关键字final:
class NoDerived final {/**/}; //NoDerived不能作为基类
五十二、虚函数的override和final指示符
在c++11中我们可以使用override关键字来说明派生类中的虚函数。这么做的好处是在使得程序员的意图更加清晰的同时让编译器可以为我们发现一些错误,后者在编程实践中显得更加重要。
如果我们使用override标记了某个函数,但该函数并没有覆盖已存在的函数,此时编译器将报错:
struct B{
virtual void f1(int) const;
virtual void f2();
void f3();
};
sttuct D1 : B{
void f1(int) const override; //正确:f1与基类中的f1匹配
void f2(int) override; //错误:B没有形如f2(int)的函数
void f3() override; //错误:f3不是虚函数
void f4() override; //错误:B没有名为f4的函数
}
五十三、继承的构造函数
在c++11新标准中,派生类能够重用其直接基类定义的构造函数。一个类只初始化它的直接基类,出于同样的原因,一个类也只继承其直接基类的构造函数。类不能继承默认、拷贝和移动构造函数。
如果派生类没有直接定义这些构造函数,则编译器将为派生类合成它们。
派生类继承基类构造函数的方式是提供一条注明了(直接)基类名的using声明语句。举个例子,我们可以重新定义Bulk_quote类,令其继承Dsic_quote类的构造函数:
class Bulk_quote : public Dsic_quote{
public:
using Dsic_quote::Disc_quote; //继承Dsic_quote的构造函数
double net_price(std::size_t) const;
};
通常情况下,using声明语句只是令某个名字在当前作用域内可见。而当作用域构造函数时,using声明语句将令编译器产生代码。对于基类的每个构造函数,编译器都生成一个与之对应的派生类构造函数。换句话说,对于基类的每个构造函数,编译器都在派生类中生成一个形参列表完全相同的构造函数。
五十四、有作用域的enum
C++包含两种枚举:限定作用域的和不限定作用域的。C++11新标准引入了限定作用域的枚举类型。定义限定作用域的枚举类型的一般形式是:首先是关键字enum class(或者等价地使用enum struct)随后是枚举类型的名字以及花括号括起来的枚举类型:
enum class open_modes{input, output, append};
定义不限定作用域的枚举类型时省略掉关键字class(或struct),枚举类型的名字是可选的:
enum color{red, yellow, green}; //不限定作用域的枚举类型
//未命名的,不限作用域的枚举类型
enum {floatPrc = 6, doublePrc = 10, double_doublePrc = 16};
如果enum是未命名的,则我们只能在定义该enum的时候定义它的对象
五十五、说明类型用于保存enum对象
尽管每个enum都定义了唯一的类型,但实际上enmu是由某种整数类型表示的。在c++11新标准中,我们可以在enum的名字后面加上冒号以及我们想在enmu中使用的类型:
enum intValue : unsigned long long {
charTyp = 255, shortTyp = 65535, intTyp = 65535,
longTyp = 4294967295UL,
};
五十六、 lambda表达式
我们可以向一个算法传递任何类别的可调用对象。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。即,如果e是一个可调用的表达式,则我们可以编写代码e(args),其中args是一个逗号分隔的一个或多个参数的列表。
目前为止,我们使用过的仅有的两种可调用对象是函数和函数指针。还有两种可调用对象:重载了函数调用运算符的类,以及lambda表达式。一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。
但与函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式:
[capture list](parameter list) -> return type{function body}
其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空),return type、parameter list和function body与任何普通函数一样,分别表示返回类型参数列表和函数体。但是与普通函数不同,lambda必须使用尾置返回来指定返回类型。
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
auto f = [] {return 42;};
此例中,我们定义了一个可调用的对象f,它不接受参数,返回42。
lambda的调用和普通函数的调用方式相同,都是使用调用运算符:
cout<<f()<<endl; //打印42
五十七、新的bitset运算符
bitset操作定义了多种检测或设置一个或多个二进制位的方法。
当bitset对象的一个或多个位置位(即,等于1)时,操作any返回true。相反,当所有位复位时,none返回true。新标准引入了all操作,当所有位置位时返回true。
五十八、正则表达式库
正则表达式(regular expression)是一种描述字符序列的方法,是一种极其强大的计算工具。我们重点介绍如何使用C++的正则表达式库(RE库),它是新标准库的一部分。
RE库定义在头文件regex中,它包含多个组件
regex 表示有一个正则表达式的类
regex_match 将一个字符序列与一个正则表达式匹配
regex_search 寻找第一个正则表达式匹配的子序列
regex_replace 使用给定格式替换一个正则表达式
sregex_iterator 迭代器适配器,调用regex_search来遍历一个string中所以匹配的子串
smatch 容器类,保存在string中搜索的结果
ssub_match string中匹配的子表达式的结果
regex_match和regex_search的参数
这些操作返回bool值,指出是否找到匹配
(seq, m, r, mft) (seq, r, mft)
在字符序列seq中查找regex对象r中的正则表达式。
seq可以是一个string、表示范围的一堆迭代器以及一个指向空字符结尾的字符数组指针。
m是一个match对象,用来保存匹配结果的相关细节。m和seq必须具有兼容的类型
mft是一个可选的regex_constants::match_flag_type值。
五十九、随机数库
程序通常需要一个随机数源。在新标准出现之前,C和C++都依赖于一个简单的C库函数rand来生成随机数。此函数生成均匀分布的伪随机数。一些应用需要随机浮点数。一些程序需要非均匀分布的数。
而程序员为了解决这些问题而视图转换rand生成的随机数的范围、类型或分布时,常常会引入非随机性。
定义在头文件random中的随机数库通过一组协作的类来解决这些问题:随机数引擎类和随机数分布类。一个引擎类可以生成unsigned随机数序列,一个分布类使用一个引擎类生成指定类型的、在给定范围内的、服从特定概率分布的随机数。
引擎: 类型,生成随机unsigned整数序列
分布: 类型,使用引擎返回服从特定概率分布的随机数
C++程序不应该使用库函数rand,而应该使用default_random_engine类和恰当的分布类对象
六十、enum的提前声明
在C++11新标准中,我们可以提前声明enum。enum的前置声明(无论隐式的还是显式的)必须指定其成员的大小。
//不限定作用域的枚举类型intValues的前置声明
enum intValues : unsinged long long; //不限定作用域,必须指定成员类型
enum class open_modes; //限定作用域的枚举类型可以使用默认成员类型int
因为不限定作用域的enum未指定成员的默认大小,因此每个声明必须指定成员的大小。对于限定作用域的enum来说,我们可以不指定其成员大小,这个值被隐式地定义为int