本文主要是介绍C++ Primer Plus第十八章笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1. 复习C++11功能
1.1 新类型
1.2 统一的初始化
1.2.1 缩窄
1.2.2 std::initializer_list
1.3 声明
1.3.1 auto
1.3.2 decltype
1.3.3 返回类型后置
1.3.4 模板别名:using
1.3.5 nullptr
1.4 智能指针
1.5 异常规范的修改
1.6 作用域内枚举
1.7 对类的修改
1.8 模板和STL方面的修改
1.9 右值引用
2. 移动语义和右值引用
2.1 需要移动语义的原因
2.2 移动构造函数
2.3 赋值
3. 新的类功能
3.1 特殊的成员函数
3.2 默认的方法和禁用的方法
3.3 委托构造函数
3.4 继承构造函数
3.5 管理虚方法:override和 final
4. Lambda函数
4.1 比较函数指针、函数符和Lambda函数
4.2 使用Lambda原因
5. 包装器
5.1 包装器function及模板的低效性
6. 可变参数模板
6.1 模板和函数参数包
6.2 展开参数包中使用递归
7. C++11新增的功能
7.1 并行编程
7.2 新增的库
7.3 低级编程
1. 复习C++11功能
1.1 新类型
C++11新增类型:
- 整型
long long
unsigned long long
- 字符表示
char16_t
char32_t
1.2 统一的初始化
C++11 扩大了用大括号起的列表(初始化列表)的适用范围。使其可用于所有内置类型
和用户定义类型(即类对象)
。列表初始化的三种方式:
使用初始化列表时,可添加等号(=),也可不添加。
int x = {5};
short quar[5] {4,5,6,2,1};
可以用于new表达式中
int *ar = new int [4] {2,4,6,7}; C++11
创建对象时,可使用大括号(而不是圆括号)括起来的列表来调用构造函数。
class Stump
{private:int roots;double weight;public:Stump(int r,rouble w) : roots(r), weight(w) {}
};
Stump s1(3,15.6); // old style
Stump s2{5,43.4}; // C++11
Stump s3{4,32.1}; // C++11
1.2.1 缩窄
-
初始化列表语法可防止缩窄,即禁止将数值赋给无法存储它的数值变量,即将值存储到比它“窄”的变量中。
-
编译器会禁止变窄类型的转换,但允许在范围内变宽的转换。
1.2.2 std::initializer_list
-
C++11中提供
initializer_list模板类
,可用作构造函数的参数。 -
如果类有接受
initializer_list
作为参数的构造函数,则初始化列表语法
就只能用于该构造函数。 -
列表中的元素必须是
同一种类型
或者可转换为同一种类型
。
1.3 声明
1.3.1 auto
-
C++11
使用auto关键字
实现自动类型推断
。 -
要求进行显式初始化,让编译器能够将变量的类型设置为初始值的类型。
-
auto关键字
也可简化模板声明
。
1.3.2 decltype
-
decltype关键字:将变量的类型声明为表达式指定的类型。
template <typename T, typename U> void ef(T t, U u) {decltype(T*U) tu;... }
1.3.3 返回类型后置
-
C++11新增函数声明语法:在函数名和参数列表后面指定返回类型。
double f1(double,int); // 传统方式 auto f2(double,int)->double; // C++11中的新语法
-
使用 decltype来指定模板函数的返回类型:
template<typename T, typename U> auto eff(T t, U u) -> decltype(T*U) {// 当编译器遇到 ef的参数列表时,T和 U不再作用域内,因此必须在参数列表后使用decltype。.... }
1.3.4 模板别名:using
解决标识符冗长或复杂的问题。
- 早期使用
typedef
- 不能用于模板部分具体化
typedef std::vector<std::string>::iterator itType;
- C++11提供
using=
- 可用于模板部分具体化
template <typename T> using arr = std::array<T,int>; // 模板部分具体化
1.3.5 nullptr
C++11 新增关键字 nullptr
来表示空指针
。它是指针类型
,不能转换为整型类型。
为向后兼容,C++11仍允许使用 0
来表示空指针,因此表达式 nullptr==0
为 true
。
1.4 智能指针
因为在程序中使用 new 分配内存,未将其及时 delete释放。引入智能指针auto_ptr
。
C++11抛弃auto_ptr
,并新增了三种智能指针:unique_ptr
、shared_ptr
和 weak_ptr
。
1.5 异常规范的修改
指出函数不会引发异常有一定的价值,所以C++11添加了关键字noexpect
:
void f875(short, short) noexpect; // 不会抛出异常
1.6 作用域内枚举
在C++11中新增一种枚举。使用 class
或者 struc
t来定义。
enum old1 {yes,no,maybe}; // 传统方式
enum class New1 {never,sometimes,often,always}; // 新方式
enum struct New2 {never,lever,sever}; // 新方式
新枚举要求进行显式限定
,以避免名称冲突
。所以在引用枚举时,则使用
New1::never; New2::never;
1.7 对类的修改
-
显式转换运算符
- 早期的自动类型转换会导致意外问题,所以在C++11中引入关键字
explicit
,以禁止单参数构造函数导致的自动转换
。
- 早期的自动类型转换会导致意外问题,所以在C++11中引入关键字
-
类内成员初始化
- 可使用等号或者大括号版本的初始化,但不能使用圆括号版本的初始化。
- 类内成员初始化,可避免在构造函数中编写重复的代码。
1.8 模板和STL方面的修改
-
基于范围的for循环
double price[5] = {4.99,10.99,6.87,7.99,8.49}; for (auto x : price)std::out << x << std::endl;
- 如果要在循环中修改数组或容器中的每个元素,可使用引用类型。
-
新的STL容器
- C++11新增STL容器:
forward_list
,unordered_map
,unordered_multimap
,unordered_set
,unordered_multiset
。 - C++11新增模板array,实例化时,可指定元素类型和固定的元素数。
- C++11新增STL容器:
-
新的STL方法
- C++11新增STL方法
cbegin()
cend()
crbegin()
crend()
- 其中
crbegin()
、crend()
是rbegin()
、rend()
的const
版本。 - 除传统的
复制构造函数
和常规赋值运算符
外,STL容器还有移动构造函数
和移动赋值运算符
。
- C++11新增STL方法
-
valarray升级
- C++11中添加了两个函数 begin() 和 end()。都接受 valarray 作为参数,并返回迭代器。
-
抛弃export
- C++98新增
- C++终止用法,但为了兼容还保留关键字 export。
-
尖括号
- C++在声明嵌套模板时不再需要使用空格将尖括号分开。
1.9 右值引用
-
左值引用
- 左值:一个表示数据的表达式,程序可获取其地址。
- 左值出现在赋值语句的左边,但修饰符的出现const使得声明的标识符,即不能给它赋值,但可获取其地址。
int n; int * pt = new int; const int b = 101; // 不能赋值, int & rn = n; int & rt = *pt; const int &rb = b; // b标识符const引用地址
-
右值引用
- C++11新增
右值引用
,使用&&
表示。右值引用可以关联到右值,即可出现在赋值表达式右边,但不能对其应用地址运算符的值
。 右值
包括:字面常量
、表达式
和返回值的函数
(函数返回的不能是引用)。- 引入右值引用的主要目的之一:实现
移动语义
。
int x = 10; int y = 23; int && r1 = 13; // 字面常量 int && r2 = x + y; // 表达式 double && r3 = std::sqrt(2.0); // 函数返回值
- C++11新增
2. 移动语义和右值引用
2.1 需要移动语义的原因
-
常规复制构造函数
- 使用
const左值引用
作为参数,使得引用关联到左值实参
。 - 可执行深复制
- 使用
-
移动构造函数
- 使用
右值引用
作为参数,将引用关联到右值实参
。 - 只调整记录
- 在将所有权转移给新对象的过程中,移动构造函数可能修改其实参,意味着
右值引用
参数不应是const
。
- 使用
2.2 移动构造函数
代码示例
2.3 赋值
- 适用于构造函数的移动语义考虑也适用于赋值运算符。
3. 新的类功能
3.1 特殊的成员函数
- 原有的4个特殊成员函数
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- C++新增的2个
- 移动构造函数
- 移动赋值运算符
这些成员函数是编译器在各种情况下自动提供的。
- 如果提供了
析构函数、复制构造函数或者复制赋值运算符
,编译器将不会自动
提供移动构造函数
和移动赋值运算符
。 - 如果提供了
移动构造函数
或移动赋值运算符
,编译器将不会自动
提供复制构造函数
和复制赋值运算符
。
3.2 默认的方法和禁用的方法
-
C++11中如果提供了
移动构造函数
,所以编译器不会自动创建默认的构造函数、复制构造函数和复制赋值构造函数。可使用关键字 default
显式声明
方法的默认版本。class Someclass {public:Someclass(Someclass &&);Someclass() = default;Someclass(const Someclass &) = default;Someclass & operator=(const Someclass &) = default;... };
-
关键字
delete
可用于禁止编译器使用特定方法。- 也可禁止特定的转换
-
关键字
default
只能用于6个特殊成员函数
,但delete
可用于任何成员函数
。
3.3 委托构造函数
- C++11 允许在一个构造函数的定义中使用另一个构造函数,这种方式称为
委托
。
Notes::Notes(int kk,double xx) : Notes(kk,xx,"Uh") {/**/}
3.4 继承构造函数
-
C++98 使用
名称空间中函数可用
的方式。 -
C++11 中使用
派生类
继承基类
的所有构造函数(默认构造函数、复制构造函数和移动构造函数除外),但不会使用与派生类构造函数的特征标匹配的构造函数
。
3.5 管理虚方法:override
和 final
-
在C++11中,可使用
虚说明符override
指出要覆盖
一个函数,将其放在参数列表后面。如果声明与基类方法不匹配,编译器将视为错误。 -
final说明符
:禁止
派生类覆盖
特定的虚方法,所以在参数列表后面加上final
。 -
override
和final
并非关键字
,而是具有特殊含义的标识符
。
4. Lambda函数
Lambda 函数,也叫做Lambda表达式。
匿名函数无需给出函数命名。
4.1 比较函数指针、函数符和Lambda函数
在C++11中,对于接受函数指针或者函数符的函数,可使用匿名函数定义作为其参数。
匿名函数与函数的区别:
- 使用
[]
替代函数名
没有声明返回类型
(返回类型相当于使用decltype根据返回值推断得到)- 如果
lambda不包含返回语句
,推断出的返回类型将为void
。
[] (int x) {return x % 3 == 0;}
当且仅当, lambda表达式完全由一条返回语句组成时,自动类型推断才管用。否则,需要使用新增的返回类型后置语法:
[] (double x)-> double{int y = x; return x - y;} // 返回类型为double
匿名函数代码示例
4.2 使用Lambda原因
四个角度来分析
- 距离
- 函数定义离使用地点近。
- 简洁
- 函数符代码比函数和lambda代码更繁琐。
- 效率
- 三种方式的相对效率取决于编译器内联。
- 功能
- lambda可访问
作用域内
的任何动态变量
- 要捕获使用的变量,可将其名称放在中括号内。
- 如果
只指定变量名
,如[z]
,则将值访问
变量 - 如果在
名称前加上 &
,如[&count],则按引用访问
变量
- 如果
[&]
按引用访问
所有动态变量[=]
按值访问
所有动态变量。
- lambda可访问
函数指针方法阻止内联
,因为编译器传统上不会内联其他地址被获取的函数,因为函数地址的概念意味着非内联函数。而函数符
和lambda
通常不会阻止内联
。
可以给lambda指定一个名称,然后使用名称来替代。
auto mod3 = [] (int x) {return x % 3 == 0;} // mod3 a name for the lambdacount1 = std::count_if(n1.begin(),n1,end(),mod3); count2 = std::count_if(n2.begin(),n2.end(),mod3);
C++中引入lambda的主要目的:能将类似于函数的表达式用做接受函数指针或函数符的函数的参数。
5. 包装器
C++提供多个包装器
(wrapper
,也叫做适配器(adapter)
)。
C++11中提供了其他的包装器:
bind
- 可替代
bind1st
和bind2nd
- 可替代
mem_fn
- 可将成员函数作为常规函数进行传递
reference_wrapper
- 能创建行为像引用但可被复制的对象
function
- 以统一的方式处理多种类似于函数的形式
5.1 包装器function及模板的低效性
模板function 是在头文件functional 中声明,从调用特征标的角度定义了一个对象,可用于包装调用特征相同的函数指针、函数对象或者lambda表达式。
// 接受一个char参数和一个int参数,并返回一个double值的任何函数指针、函数对象或者lambda表达式赋值给它 std::function<double>(char, int) > fdci;
6. 可变参数模板
要创建可变参数模板,需要理解以下要点:
- 模板参数包(parameter pack)
- 函数参数包
- 展开参数包
- 递归
6.1 模板和函数参数包
C++11中提供省略号表示的元运算符(meta-operator)。
模板参数包
:可以声明表示模板参数包
的标识符,模板参数包是一个类型列表
。函数参数包
:可以声明表示函数参数包
的标识符,函数参数包是一个值列表
。
// Args可以与任意数量的类型匹配
template <typename... Args> // Args 是模板参数包
void show_list(Args...args) // args 是函数参数包
{...
}
6.2 展开参数包中使用递归
将函数参数包展开,对列表中的第一项进行处理,再将余下的内容传递给递归调用,以此类推,直到列表为空。
template <typename T ,typename...Args>
void show_list(T value, Args..args)
{show_list(args...);
}// 对于按值传递来说,效率低,所以可更改为;
void show_list1(const T& value, const Args&... args)
{show_list1(args...);
}
7. C++11新增的功能
7.1 并行编程
为解决并行性
的问题,C++定义了一个支持线程化执行
的内存模型,添加了关键字 thread_local
,提供了相关库支持。
关键字 thread_local
将变量声明为静态存储
,其持续性和特定线程相关,即定义变量的线程过期
时,变量也过期
。
库支持由原子操作(automic、operation)库
和线程支持库
组成。原子操作库提供的头文件:
thread
mutex
condition
variable
future
7.2 新增的库
在C++11中新增专用的头文件库
- random:随机数扩展工具
- chrono:处理时间间隔的途径
- tuple:支持模板tuple。
- ratio:编译阶段的有理数算术库
- regex:正则表达式库。用于指定与文本字符串的内容匹配。
7.3 低级编程
低级编程中的“低级”指的是抽象程度,而不是编程质量。
C++11给低级编程人员提供的两个便利:
- 放松了
POD(Plain Old Data)
的要求。 - 允许
共用体的成员
有构造函数
和析构函数
。
这篇关于C++ Primer Plus第十八章笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!