13.6-14.8读书笔记

13.6 对象移动

13.6.1 右值引用

概念: 为了支持移动操作,新标准引入了的一种新的引用类型.所谓右值引用就是必须绑定到右值的引用. 通过&&来获得右值引用

    int i = 42;
    int &r = i;
    int &&rr = i;     // 错误,不能将一个右值引用绑定到一个左值上
    int &r3 = i * 42; // 错误,i*42是个右值
    const int &r3 = i * 42;
    int &&rr2 = i * 42;
标准库move函数

虽然不能将一个右值引用直接绑定到一个左值上,但我们可以显式地将一个左值转换为对应的右值引用类型. 可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用

    int &&rr1 = 42;
    int &&rr3 = std::move(rr1);
移动构造函数和移动赋值运算符

移动构造函数的第一个参数是该类类型的一个引用.这个引用参数在移动构造函数中是一个右值引用

// 移动构造函数不分配任何的新内存,它接管给定的StrcVec中的内存,在接管内存之后,它将给定对象中的指针都置为nullptr
StrVec::StrVec(StrVec &&s) noxcept 
: elements(s.elements), first_free(s.first_free), cap(s.cap)
{
	s.elements = s.first_free = s.cap = nullptr;
}

移动赋值运算符执行与析构函数和移动构造函数相同的工作

StrVec &StrVec::opeartor = (StrVec &&rhs) noexcept 
{
	if (this != &rhs) {
		free();
		elements = rhs.elements;
		first_free = rhs.first_free;
		cap = rhs.cap;
		ths.elements = rhs.first_free = rhs.cap = nullptr;
	}
	return *this;
}

合成的移动操作
只有一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时候, 编译器才会为他合成移动构造函数或移动赋值运算符

struct X{
	int i ; //内置类型可以移动
	std::string s;
};

struct hasX {
	X mem;
};

X x, x2 = std::move(x);// 使用合成的移动构造函数
hasX hx, hx2 = std::move(hx);// 使用合成的移动构造函数

重载和引用函数
class Foo {
public:
    Foo &operator=(const Foo&) & {
        return *this;
    }
    // 可用于可改变的右值
    Foo sorted() && {
        sort(data.begin(), data.end());
        return *this;
    }
	// 可用于任何类型的foo
    Foo sorted() const & {
        Foo ret(*this);
        sort(ret.data.begin(), ret.data.end());
        return ret;
    }


private:
    vector<int> data;
};

Foo &retFoo() {

    Foo tmp;
    Foo &ret = tmp;
    return ret;
}

Foo retVal() {
    Foo ret;
    return ret;
}
int main()
{
    retVal().sorted();
    retFoo().sorted();
    return 0;
}

14.1 基本概念

在这里插入图片描述

在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?

我们可以直接调用重载运算符函数。重置运算符与内置运算符有一样的优先级与结合性。

ring 和 vector 都定义了重载的== 以比较各自的对象,假设 svec1 和 svec2 是存放 string 的 vector,确定在下面的表达式中分别使用了哪个版本的==?
a) “cobble” == “stone”
(b) svec1[0] == svec2[0]
(c ) svec1 == svec2
(d) "svec1[0] == “stone”

(a) 都不是。
(b) string
(c ) vector
(d) string

14.2 输入和输出运算符

14.2.1 重载输出运算符<<

第一个形参是一个非常量的ostream对象的引用, 第二个形参一般来说是一个常量的引用; 一般要返回它的ostream形参

ostream &operator<<(ostream &os, const Sales_data &item) {
	os << item.isbn() << " " << item.units_old << " " << item.revenue << " " << item.avg_price();
	return os;
}

输入输出运算符必须是非成员函数, 而不能是类的成员函数. 假设输入输出运算符是某个类的成员,则它们也必须是istreamostream的成员. 然而,这两个类属于标准库,并且我们无法为标准库中的类添加任何成员

14.2.2 重载输入运算符

第一个形参运算符将要读取的流的引用,第二个形参是将要读入到的(非常量)对象的引用

istream &operator >> (istream &is, Sales_data &item) {
	double price;
	is >> item.bookNo >> item.unites_sold >> price;
	if (is) {
		item.revenue = item.units_sold * price;
	} else {
		item = Sales_data();
	}
	return is;
}

14.3 算术和关系运算符

通常,我们把算术和关系运算符定义非成员函数以允许对左侧或右侧的运算对象进行转换;

Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) {
	Sales_data sum = lhs;
	sum += rhs;
	return sum;
}
14.3.1 相等运算符

用来检验两个对象是否相等

bool operator ==(const Sales_data &lhs, const Sales_data &rhs) {
	return lhs.isbn() == rhs.isbn() && lhs.units_sold == rhs.unites_sold && lhs.revenue == rhs.revenue;
}
bool operator !=(const Sales_data &lhs, const Sales_data &rhs) {
	return !(lhs == rhs)	
}

14.4 赋值运算符

和拷贝赋值及移动赋值运算符一样,其他重载赋值运算符也必须释放当前内存空间,再创建一片新空间.

StrVec &StrVec::operator=(initialize_list<string> il)
{
	auto data = alloc_n_copy(il.begin(), il.end());
	free();
	elements = data.first;
	first_free = cap = data.second;
	return *this;
}

复合赋值运算符

Sales_data &Sales_data::operator += (const Sales_data &rhs) {
	units_sold += rhs.units_sold;
	revenue += rsh.revenue;
	return *this;
}

14.5 下标运算符

表示容器的类通常可以通过在容器中的位置访问元素, 这些类一般会定义下标运算符[], 下标运算符必须是成员函数

class StrVec {
public:
	std::string &operator[](std::size_t n) { return elements[n];}
	const std::string &operator[](std::size_t n) const { return elements[n];}
private:
	std::string *elements;
}

14.6 递增和递减运算符

两种运算符使得类可以在元素的序列中前后移动. C++语言并不要求递增和递减运算符必须是类的成员,但是因为它们改变的正好是所操作对象的状态,所以建议将其设定为成员函数

class StrBlobPtr {
public:
	StrBlobPtr &operator++(); //前置运算符
	StrBlobPtr &operator--();	
};

区分前置和后置运算符
后置版本接受一个额外的(不被使用)int类型的形参.

class StrBolbPtr {
public:
	StrBlobPtr operator++(int); //后置运算符
	StrBlobPtr operator--(int);	
};

显式地调用后置运算符

StrBlobPtr p(a1);
p.operator++(0); // 后置版本的operator++;
p.operator++();  // 前置版本的operator++;

14.7 成员访问运算符

class StrBlobPtr {
	sdt::string &operator*() const {
		auto p = check(curr, "dereference past end");
		return (*p)[curr];
	}
	std::string *operator->() const {
		return & this->operator*();
	}
};

14.8 函数调用运算符

如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象

struct absInt {
	int operator()(int val) const {
		return val < 0 ? -val : val;
	}
}
int i = -42;
absInt absObj;
int ui = absObj(i);
14.8.1 lambda是函数对象

lambda表达式产生的类中含有一个重载的函数调用运算符

string words;
stable_sort(word.begin(), word.end(), [](const string &a, const string &b) 
{return a.size() < b.size();});

// 其行为类似于下面这个未命名对象
class ShorterString {
public:
	bool operator() (const string &s1, const string &s2) const {
		return s1.size() < s2.size();
	}
};

// 用类代替lambda表达式后
stable_sort(word.begin(), word.end(), ShorterString());
14.8.2 标准库定义的函数对象

在这里插入图片描述

14.8.3 可调用对象与function

不同类型可能具有相同的调用形式

// 普通函数
int add(int i, int j) {return i + j;}
// lambda,产生一个未命名函数对象类
auto mod = [](int i, int j){ return i % j;}
// 函数对象类
struct divide {}
	int operator()(int denominator, int divisor) {
		return denominator / divisor;
	}
;

// 构建从运算符到函数指针的映射关系,其中函数接受两个int,返回一个int
map<string, int(*)(int, int)> binops;

但是我们不能将mod或者divide存入binops,因为mod是个lambda表达式,而每个lambda有它自己的类类型,与存储在binops中的值的类型不匹配
标准function
在这里插入图片描述

map<string, function<int(int,int)>> binops = {
	{"+", add},
	{"-", std::minus<int>()},
	{"/" devide()},
	{"*", [](int i, int j){return i * j;}},
	{"%", mod}
};

我们的map中包含5个元素,尽管其中可调用的类型各不相同,我们仍然能够把所有这些类型都存储在一个function<int(int,int)>类型中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值