C++ Primer 第Ⅲ部分笔记——类设计者的工具

对象移动

右值引用

右值引用区别于普通引用,用两个&表示

返回左值引用的函数,连同赋值、下标、解引用和前置递增递减运算符返回左值

返回非引用的函数,连同算术、关系、位以及后置递增递减运算符都生成右值

我们不能将左值引用绑定到一个右值上,但可以使用const左值引用或右值引用绑定到一个右值上

左值持久,右值短暂

我们不能将右值引用绑定到一个变量上

标准库move函数

位于头文件utility

int &&i1 = std::move(r2);

我们可以使用move函数将左值强制转换为右值

这也意味着,我们使用move函数之后可以销毁对象,也可以给对象赋予一个新值,但我们不能使用这个对象了

移动构造函数和移动赋值运算符

移动构造函数的参数为:类名&&

这里引入一个新名词 noexcept 不抛出任何异常,在参数列表后添加

我们在移动构造函数中必须另类中的数据成员回归成可析构

移动赋值运算符函数类似,但前提是要检查返回值和传进来的右值地址是否相同

移动迭代器

函数make_move_iterator函数接受一个迭代器,使其成为一个移动迭代器,将其与普通的迭代器使用即可

区别在于我们使用这个函数后,这个迭代器以后就无法使用了,另外使用时会触发类的移动构造函数

重载与调用函数对象

重载后置++–

vec operator++(int)
{
     vec ret = *this;
     ++*this;
     return ret;
}

函数调用重载

class add
{
public:
     int operator() (int i,int j)
     {
          return i +j;
     }
};
add Add;
int i = Add(2,4);

lambda是函数对象

//假设有这么一个算法表达式
stable_sort(words.begin(),words.end(),[](const string&a,const string &b) { return a.size()<b.size(); });
//其行为等价于
class ShorterString
{
public:
      bool operator()(const string&a,const string&b) const {return a.size()<b.size();}
};
stable_sort(words.begin(),words.end(),ShorterString());

标准库定义的函数对象

头文件:functional

plus<int>add;
int i = add(2,4);
sort(vec.begin(),vec.end(),greater<int>());//按降序排序

可调用对象于function

int (int,int);//是一个函数类型,接受两个int,返回一个int
int mod (int i,int j);//普通函数
auto add = [](int i,int j) {return i + j;};//lambda
struct divide{
    int operator() (int i,int j);//函数对象类
};

我们可以通过标准库function来统一这三种类型

比如我们需要做一个桌面计算器

map<string,function<int(int,int)>>cal;//定义一个map
cal["+"] = add;
cal["%"] = mod;
cal["/"] = divide();

但我们必须注意二义性的问题

如有必要,必须这样做

int (*p)(int,int)=add;
cal["+"] = p;

重载,类型转换与运算符

类型转换运算符

基本形式:

operator type() const ;

类继承

虚函数

虚函数通常在基类前声明virtual

为了防止派生类的虚函数并未覆盖基类中的虚函数,我们必须在派生类虚方法的参数列表后加上 override

如果将一个函数设为final,那么它的派生类不可覆盖他

回避虚函数的机制

必须显式说明,Base::fun();

抽象基类

若一个基类有一个函数在参数列表后加上了 =0

那么该基类成为抽象基类

抽象基类不能显式声明

派生类与基类的转换

假设D继承B,有三种情况

只有公有继承能让用户能直接使用派生类转换为基类

任何继承方式,D的成员函数和友元都可以使用派生类转换为基类

只有公有与保护继承,D的成员和友元可以使用转换