Chapter 4 How to Benefit from Move Semantics

1. Avoid Objects with Names

①如果编译器检测到一个值 来自于一个生命周期即将结束的对象,那么他便会自动的去选择移动语意
传递临时对象
返回一个局部变量 return local object by value
②除此之外我们还可以显示标记一个对象 std::move()

尽量避免给对象命名

MyType x{42, "hello"};
foo(x); // x not used afterwards

这个x对象,以后就没有使用过了,那么我们就可以用另一种方式代替

foo(MyType{42,"hello"});

当然这种做法可能被程序的可维护性 可读性这些需求产生冲突
当不能避免的时候,就使用std::move()吧

2. Avoid Unnecessary std::move()

按值返回局部对象,通常会自动使用移动语意来进行优化
有些程序员可能就想使用std::move()来强制执行

std::string foo()
{
std::string s;
...
return std::move(s); // don’t do this
}

表达式产生的类型是 std::string&&, 与函数返回值的类型不同了
std::move() is just a static_cast.

std::string s{std::move(foo())};//don’t do this

std::string s{foo()};//instead of

3.Initialize Members with Move Semantics

①Initialize Members the Classical Way

#include <string>
class Person
{
private:
	std::string first; // first name
	std::string last; // last name
public:
	Person(const std::string& f, const std::string& l)
	: first{f}, last{l} {}
}
Person p{"Ben", "Cook"};
  1. 编译器发现初始化是由提供的构造函数实现的,但是参数的类型不匹配,所以会创建出两个临时的std::string 对象,和形参f l 绑定
  2. 每个std::string成员调用其std::string的拷贝构造函数(拷贝形参f 和 l)来进行初始化
  3. 然后在Person构造函数的结束,临时对象被销毁
    有四次内存的分配 只有两次是必须

/****************************************/
②Initialize Members via Moved Parameters Passed by Value

#include <string>
class Person
{
private:
	std::string first; // first name
	std::string last; // last name
public:
	Person(std::string f, std::string l)
		: first{std::move(f)}, last{std::move(l)} {}
}

注意函数的形参不再是引用了,而是value

std::string name1{"Jane"}, name2{"White"};

Person p{name1, name2}; 
// OK, copy names into parameters and move them into the members

Person p{std::move(name1), std::move(name2)}; 
// OK, move value to parameters and then to members

/*************************************************/
③Using Rvalue References
为了支持move语意,形参必须声明为 non-const的右值引用

class Person {
...
Person(std::string&& f, std::string&& l)
: first{std::move(f)}, last{std::move(l)} {
}
...
};

右值引用的局限性,不能传递那些后面仍然需要该值的对象

Person p1{"Ben", "Cook"}; // OK
std::string name1{"Jane"}, name2{"White"};
...
Person p2{name1, name2};
 // ERROR: can’t pass a named object to an rvalue reference

4.应对多种的构造函数

class Person {
private:
std::string first; // first name
std::string last; // last name
public:
Person(const std::string& f, const std::string& l)
: first{f}, last{l} {}

Person(const std::string& f, std::string&& l)
: first{f}, last{std::move(l)} {}

Person(std::string&& f, const std::string& l)
: first{std::move(f)}, last{l} {}

Person(std::string&& f, std::string&& l)
: first{std::move(f)}, last{std::move(l)} {}

Person(const char* f, const char* l)
: first{f}, last{l} {}

Person(const char* f, const std::string& l)
: first{f}, last{l} {}

Person(const char* f, std::string&& l)
: first{f}, last{std::move(l)} {}

Person(const std::string& f, const char* l)
: first{f}, last{l} {}

Person(std::string&& f, const char* l)
: first{std::move(f)}, last{l} {}
...};

5.Summary for Member Initialization

初始化具有移动语意的成员:
①形参 不应该使用左值引用lvalue reference,而应该是值传递 value,然后对形参调用move语意
②或者重载move语意的构造函数

class Person 
{
private:
	std::string name;
	std::vector<std::string> values;
public:
	Person(std::string n, std::vector<std::string> v)
	: name{std::move(n)}, values{std::move(v)} 
	{
	}
...
};

但是如果有一个成员是std::array的话,最好是重载构造函数,因为移动一个array需要大量的时间

class Person {
private:
	std::string name;
	std::array<std::string, 1000> values;
public:
	Person(std::string n, const std::array<std::string, 1000>& v)
	: name{std::move(n)}, values{v} 
	{
	}
	Person(std::string n, std::array<std::string, 1000>&& v)
	: name{std::move(n)}, values{std::move(v)}
	 {
	}
...
};

6.Should we now Always Pass by Value and Move

看下面的一个例子:

class Person {
private:
	std::string first; // first name
	std::string last; // last name
public:
	Person(std::string f, std::string l)
	: first{std::move(f)}, last{std::move(l)} 
	{
	}
...
	void setFirstname(std::string s)
	 {
		first = std::move(s);
	}
...
};

产生了如下的调用

Person p{"Ben", "Cook"};
std::string name1{"Ann"};
std::string name2{"Constantin Alexander"};
p.setFirstname(name1);
p.setFirstname(name2);
p.setFirstname(name1);
p.setFirstname(name2);

每次我们调用set这个函数时,便会产生一个临时对象(这里是形参),然后对该临时对象使用move给first,那么我们就会产生四次的内存分配
/*************************************************************/
一种新的实现

void setFirstname(const std::string& s) 
{
	first = s;
}

first已经被构造函数初始化过了,他在堆中占据了一块内存,如果当前的字符串s没有超过first原本的长度,那么就不用重新分配内存了,而且传入的是引用,我们也没有产生临时的对象

7.Move Semantics in Class Hierarchies

暂时看不懂

8.总结

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值