cv_gordon

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::

C++ 2.0的内容分为2部分讲解:

一, 新的语法;

二, 标准库中新的内容;

 

一、 新的语法

1. 声明一个类时,C++会默认生成big five成员函数。

默认构造函数,默认析构函数,默认拷贝构造,默认赋值构造,默认移动构造(移动拷贝构造,移动赋值构造)。

默认构造函数的作用:调用当前类的父类构造函数。

默认拷贝构造函数的作用:将非静态的成员变量逐个进行复制。若成员变量中存在指针,执行的是浅拷贝。因此,定义一个新类时,若成员变量中村子指针,则需要重新定义big three函数(默认拷贝构造,默认赋值构造,默认析构),将浅拷贝改为深拷贝。

 

 

2. 一般情况下,构造函数都是public权限。但是,若构造函数声明为private权限,则只有编译器有权限调用该构造函数,任何用户都无权限调用。

 

3. 关键字 decltype

作用:let the compiler find out the type of an expression.

应用:a) 声明返回类型;b) 元编程; c) 获取lambda函数的类型;

a)利用decltype提供一种返回类型的声明方法。

template <typename T1, typename T2>

auto add(T1 x, T2 y) -> decltype(x+y); 

 

4. lambdas 函数

lambdas函数可以视为函数对象(function object),其对应关系可以用如下两个例子展示。

例子1

 

 例子2

 

5. 可变模板

变化的是模板参数。参数的数量和类型都会变化。

利用参数个数逐一递减的特性,实现递归函数调用。

 

可变模板例子 1)

函数1是递归终止条件;

函数2和函数3可以并存,但是编译器会优先使用更为特化的函数2;

 

可变模板例子 3)

利用可变模板实现取最大值的功能。

右下角的代码,结构体 _Iter_less_iter,成员函数重载()。_Iter_less_iter(),是调用了默认构造函数?还是调用了重载函数()?

侯捷介绍:_Iter_less_iter 重载了(),因此对象可以像函数一样被调用。

 

可变模板例子 6)

tuple的构造函数利用可变模板实现递归继承,从而完成tuple对象的初始化。

构造函数中,在初始化列表中,调用父类构造函数 inherited(vtail...)。

使用private继承,其目的在于仅仅使用父类对象的内存空间。

存疑地方,typename的使用 typename Head::type head() { return m_head; }

 

可变模板例子 7)

利用可变模板实现tuple类型的对象初始化。递归复合的内存示意图如右上角所示,x<T1, T2, T3>中包含x<T2, T3> ,,,

 

 

二、 标准库新的内容

本节的教学目标:自己设计一个 move aware class,可以执行move操作。

 

右值引用 rvalue references

作用:右值引用可以优化一些不必要的拷贝操作,极大提升代码的效率。

1) 临时对象就是一种右值;

2) 右值只能出现在等号右边;

 

编译器可以识别函数名为函数所在的内存地址起点,即函数所在的地址。

注意:如下代码段中,foo为函数名;foo() 代表函数返回的东西,是一个右值,右值不可寻址。

 

 

如下例子,通过右值引用避免拷贝操作。

需要满足如下条件:

1)调用端告诉编译器,这是一个右值。可以是临时对象 或者 使用std::move()将左值转换为右值;

2)被调用端写出一个专门处理rvalue的移动赋值函数,例如 c.insert(..., &&x)。形参中的右值引用要求元素类型必须要有对应移动构造函数(与之相对应,拷贝构造函数都是深拷贝操作)。

 

上图代码中, c,c1,c2都是 std::vector<MyString> 类型。

其中 M c1(c); 执行深拷贝;  M c2(std::move(c1)); 执行vector中3个指针的交换(swap)。

3个指针包括,vector的内存首指针,内存尾指针和数据尾指针。

 

被调用端

如图所示,G4.9 是 C++ 2.0。其中,vector.insert()添加了专门处理右值引用的版本(move aware)。

 

C++ 2.0 中新版本的 vector.insert(..., && x) 要求vector中存储的元素要有move aware的构造函数,包括移动构造和移动赋值。这样的元素类型才能进行移动操作,节省拷贝操作。

 

完美传递 和 不完美传递

如下 forward(2);  和  forward(move(a)); 所示,输入右值参数,在forward() 函数内部调用proces() 函数时,却变成了左值。这是不完美的传递。

 

 

C++2.0 提供完美传递 std::forward<>(),可以保持参数的特性,包括modifiable,const,lvalue 和 rvalue。

 

设计一个move aware class, MyString。

MyString 的成员中有指针。针对指针变量,移动构造相比拷贝构造节省了拷贝构造操作。

拷贝构造函数执行深拷贝;移动构造函数执行浅拷贝,同时把临时对象的指针置为nullptr。

在析构函数中,判断临时对象的指针 _data 是否为空?若不为空,则进行delete操作。

 

 

 

标准库的数据结构

红色方框标识出C++2.0新的数据结构

 

hashtable 

篮子的大小 A=53,是一个质数。一个元素,对应一个hash code,它在hash表中存放位置 p 的计算方法为:

hash code % A = p

举个例子,55 % 53 = 2.

当元素数量大于篮子数量时,扩充篮子数量至 2A 左右(取2A附近的最近质数),然后重新计算每个元素在hashtable中的存储位置。

 

 

在实际情况中,hashtable中存放的是对象。因此,要利用 hash function 计算每个对象相应的 hash code。

如下所示,计算一些基本数据类型的 hash code。hash code的特点要够杂够乱够随机,可以避免在 hashtable 中存放的冲突。

 

 

对于 hash<int>()(123) ,第一个小括号表示创建一个临时对象,是一个 function object。第二个小括号表示调用这个function object,其行为类似于调用函数,传入参数123.

 

上述代码的底层实现原理如下,先创建一个泛化的 hash 结构体,再针对每一种基本数据类型进行特化。

 

 

 

利用hash function计算字符串的hash code

 

posted on 2020-02-16 14:50  cv_gordon  阅读(975)  评论(0编辑  收藏  举报