C++ Primer 第Ⅲ部分笔记——类设计者的工具
1.对象移动
1.1右值引用
右值引用区别于普通引用,用两个&表示
返回左值引用的函数,连同赋值、下标、解引用和前置递增递减运算符返回左值
返回非引用的函数,连同算术、关系、位以及后置递增递减运算符都生成右值
我们不能将左值引用绑定到一个右值上,但可以使用const左值引用或右值引用绑定到一个右值上
左值持久,右值短暂
我们不能将右值引用绑定到一个变量上
1.2 标准库move函数
位于头文件utility
int &&i1 = std::move(r2);
我们可以使用move函数将左值强制转换为右值
这也意味着,我们使用move函数之后可以销毁对象,也可以给对象赋予一个新值,但我们不能使用这个对象了
1.3移动构造函数和移动赋值运算符
移动构造函数的参数为:类名&&
这里引入一个新名词 noexcept 不抛出任何异常,在参数列表后添加
我们在移动构造函数中必须另类中的数据成员回归成可析构
移动赋值运算符函数类似,但前提是要检查返回值和传进来的右值地址是否相同
1.4移动迭代器
函数make_move_iterator函数接受一个迭代器,使其成为一个移动迭代器,将其与普通的迭代器使用即可
区别在于我们使用这个函数后,这个迭代器以后就无法使用了,另外使用时会触发类的移动构造函数
2.重载与调用函数对象
2.1 重载后置++--
vec operator++(int)
{
vec ret = *this;
++*this;
return ret;
}
2.2函数调用重载
class add
{
public:
int operator() (int i,int j)
{
return i +j;
}
};
add Add;
int i = Add(2,4);
2.3 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());
2.4标准库定义的函数对象
头文件:functional
plus<int>add;
int i = add(2,4);
sort(vec.begin(),vec.end(),greater<int>());//按降序排序
2.5可调用对象于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;
3.重载,类型转换与运算符
3.1类型转换运算符
基本形式:
operator type() const ;
4.类继承
4.1虚函数
虚函数通常在基类前声明virtual
为了防止派生类的虚函数并未覆盖基类中的虚函数,我们必须在派生类虚方法的参数列表后加上 override
如果将一个函数设为final,那么它的派生类不可覆盖他
4.2回避虚函数的机制
必须显式说明,Base::fun();
4.3抽象基类
若一个基类有一个函数在参数列表后加上了 =0
那么该基类成为抽象基类
抽象基类不能显式声明
4.4派生类与基类的转换
假设D继承B,有三种情况
只有公有继承能让用户能直接使用派生类转换为基类
任何继承方式,D的成员函数和友元都可以使用派生类转换为基类
只有公有与保护继承,D的成员和友元可以使用转换