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"};
- 编译器发现初始化是由提供的构造函数实现的,但是参数的类型不匹配,所以会创建出两个临时的std::string 对象,和形参f l 绑定
- 每个std::string成员调用其std::string的拷贝构造函数(拷贝形参f 和 l)来进行初始化
- 然后在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
暂时看不懂