1. Move Semantics in Ordinary Classes
①C++11之后,编译器会为简单的类生成 移动构造函数 和 移动赋值运算符
(就像生成的默认构造函数和默认赋值运算符)
②什么时候会使用移动构造
- 当返回一个局部变量(return local obj by value)
- 按值传递一个无名对象
- 传递一个临时对象(例如 函数的返回值)
- 被std::move 标记的对象
③测试代码
一个简单的类 只有构造函数和一个重载的运算符
class Test
{
public:
Test(const std::string& Temp) : str{Temp} {};
friend ostream& operator<<(ostream& os, const Test& Temp)
{
os << Temp.str << endl;
return os;
}
private:
std::string str;
};
int main()
{
Test a("12345678910111213");
cout <<"a " << a << endl;
cout << "***************" << endl;
Test b = a;
cout << "a " << a << endl;
cout << "b " << b << endl;
cout << "***************" << endl;
Test c = std::move(a);
cout << "a " << a << endl;
cout << "b " << b << endl;
cout << "c " << c << endl;
return 0;
}
我们可以看到 a的值已经被move给了c了
Test d = {std::move(b)};
cout << "a " << a << endl;
cout << "b " << b << endl;
cout << "c " << c << endl;
cout << "d " << d << endl;
我们可以看到 大括号初始化和赋值初始化中使用std::move确实被移动了
2.什么时候编译器不会自动生成这两个函数呢?
编译器得保证它自动生成的函数是正确的,符合用户需求的,所以编译器会受到一些限制。
①当类中有拷贝构造函数的时候
我们以前知道,对一个对象使用std::move,当该类中的构造函数没有存在右值引用的形参的时候X&&,他便会去找形参是const X&的构造函数
②当类中有拷贝赋值运算符
③其他的移动操作
④析构函数
基类有个虚的析构函数,但是子类没有析构函数,子类会生成这两个函数
/*****************************************************/
当发生上面情况的时候,那么编译器不会为我们生成移动函数了,所以我们需要注意
①可能会调用拷贝函数,得到的是我们不期望的结果
比如直接拷贝指针 进行了浅拷贝
②类的成员对值有要求
③对象没有默认的构造状态
3.Implementing Special Copy/Move Member Functions
// move constructor (move all members):
Customer(Customer&& cust)
: name{std::move(cust.name)}, values{std::move(cust.values)}
{
std::cout << "MOVE " << name << \n;
}
// move assignment (move all members):
Customer& operator= (Customer&& cust)
{
std::cout << "MOVEASSIGN " << cust.name << \n;
if (this != &cust)
{ // move assignment to itself?
name = std::move(cust.name);
values = std::move(cust.values);
}
return *this;
}
4. Rules for Special Member Functions
① 如果用户没有自定义构造函数,那么就会有默认构造函数
②拷贝成员函数 和析构函数 会禁止生成默认的移动成员函数
③move成员函数 会禁止生成默认的 copy成员函数
Deleting Moving Makes No Sense
class Person {
public:
...
// NO copy constructor declared
// move constructor/assignment declared as deleted:
Person(Person&&) = delete;
Person& operator=(Person&&) = delete;
...
};
你禁用了move,但是当类中声明了move成员函数,copy成员函数也被禁用了!
Person p{"Tina", "Fox"};
coll.push_back(p); // ERROR: copying disabled
coll.push_back(std::move(p)); // ERROR: moving disabled
/***************************************************/
Disabling Move Semantics
class Customer {
...
public:
...
Customer(Customer&&) = delete; // OOPS: does disable move calls
Customer& operator=(Customer&&) = delete; // OOPS: does disable move calls
};
这虽然是可以禁止移动语意,但是通常不是合适的做法
class Customer {
...
public:
...
Customer(const Customer&) = default; // disable move semantics
Customer& operator=(const Customer&) = default; // disable move semantics
};
上面是通常的做法
/***********************************************************/
Moving for Members with Disabled Move Semantics
class Customer {
...
public:
...
Customer(const Customer&) = default; // copying calls enabled
Customer& operator=(const Customer&) = default; // copying calls enabled
Customer(Customer&&) = delete; // moving calls disabled
Customer& operator=(Customer&&) = delete; // moving calls disabled
};
class Invoice
{
std::string id;
Customer cust;
public:
... // no special member functions
};
Invoice i;
Invoice i1{std::move(i)}; // OK, moves id, copies cust
默认生成的拷贝函数和移动函数,会根据成员是否能移动或能拷贝做出不同的行为
5.Exact Rules for Generated Special Member Functions
我们有如下的派生类
class MyClass : public Base
{
private:
MyType value;
...
};
/************************************************/
Copy Constructor
拷贝构造函数自动生成的条件,是在没有用户没有自定义移动构造函数
和 移动赋值运算符的时候
通常像下面这样,基类的构造函数总是先执行
MyClass(const MyClass& obj)
: Base{obj}, value{obj.value}
{
}
/**********************************************/
Move Constructor
move构造函数 自动生成的条件
用户没有自定义拷贝构造函数,拷贝赋值运算符,移动赋值运算符,析构函数
通常像下面这样,总是保证基类的先被移动构造,如果一个类不能被移动但是能够被拷贝的话,那么就用拷贝代替移动
MyClass(MyClass&& obj)
: Base{std::move(obj)}, value{std::move(obj.value)} {
}
/**********************************************/
Copy Assignment Operator
拷贝赋值运算符 自动生成的条件
用户没有自定义移动构造函数 和 移动赋值运算符
MyClass& operator= (const MyClass& obj) {
Base::operator=(obj); // - perform assignments for base class members
value = obj.value; // - assign new members
return *this; }
生成的拷贝赋值运算符 总是保证基类的成员先被赋值
/**********************************************/
Move Assignment Operator
move赋值运算符生成的条件
用户没有自定义 拷贝构造函数 拷贝赋值运算符 移动构造函数 析构函数
MyClass& operator= (MyClass&& obj) {
Base::operator=(std::move(obj));
// - perform move assignments for base class members
value = std::move(obj.value); // - move assign new members
return *this; }
生成的移动赋值运算符,总是保证基类的成员先被移动,要是不能被移动赋值但是可以被拷贝赋值,那就用拷贝赋值代替