C++11之后出现了移动构造函数这么一个东西,stl里面的类我的理解vector list都有去实现这个东西。
现在有几个问题需要确认:
- 移动构造函数的实现方式
- 默认移动构造函数是否会被调用
- 移动构造函数的使用场景
#include <iostream>
class MemoryBlock
{
public:
// 构造器(初始化资源)
explicit MemoryBlock(size_t length)
: _length(length)
, _data(new int[length])
{
}
// 析构器(释放资源)
~MemoryBlock()
{
if (_data != nullptr)
{
delete[] _data;
}
}
// 拷贝构造器(实现拷贝语义:拷贝that)
MemoryBlock(const MemoryBlock& that)
// 拷贝that对象所拥有的资源
: _length(that._length)
, _data(new int[that._length])
{
std::copy(that._data, that._data + _length, _data);
}
// 拷贝赋值运算符(实现拷贝语义:释放this + 拷贝that)
MemoryBlock& operator=(const MemoryBlock& that)
{
if (this != &that)
{
// 释放自身的资源
delete[] _data;
// 拷贝that对象所拥有的资源
_length = that._length;
_data = new int[_length];
std::copy(that._data, that._data + _length, _data);
}
return *this;
}
// 移动构造器(实现移动语义:移动that)
MemoryBlock(MemoryBlock&& that)
// 将自身的资源指针指向that对象所拥有的资源
: _length(that._length)
, _data(that._data)
{
// 将that对象原本指向该资源的指针设为空值
that._data = nullptr;
that._length = 0;
}
// 移动赋值运算符(实现移动语义:释放this + 移动that)
MemoryBlock& operator=(MemoryBlock&& that)
{
if (this != &that)
{
// 释放自身的资源
delete[] _data;
// 将自身的资源指针指向that对象所拥有的资源
_data = that._data;
_length = that._length;
// 将that对象原本指向该资源的指针设为空值
that._data = nullptr;
that._length = 0;
}
return *this;
}
private:
size_t _length; // 资源的长度
int* _data; // 指向资源的指针,代表资源本身
};
MemoryBlock f() { return MemoryBlock(50); }
int main()
{
MemoryBlock a = f(); // 调用移动构造器,移动语义
MemoryBlock b = a; // 调用拷贝构造器,拷贝语义
MemoryBlock c = std::move(a); // 调用移动构造器,移动语义
a = f(); // 调用移动赋值运算符,移动语义
b = a; // 调用拷贝赋值运算符,拷贝语义
c = std::move(a); // 调用移动赋值运算符,移动语义
}
上面代码中有移动构造函数的实现方式,就是把原来对象里面的成员变量给到当前对象,然后把原对象里面的成员置空,就是把A的东西搬运到B里面,然后A里面就没有东西,其实我觉得A里面的东西置空不置空无所谓,这个要说右值构造函数的调用时机。
右值构造函数的调用时机
#include <iostream> // std::cout
class A {
public:
A() {
cout << "A::A() 构造函数" << this << endl;
str = "A";
}
~A() {
cout << "析构函数" << this << endl;
}
A(const A &a) {
cout << "拷贝构造函数" << this << endl;
str = a.str;
}
A& operator=(const A& a) {
cout << "赋值运算符" << this << endl;
str = a.str;
return *this;
}
A(const A&& a)
: str(a.str) {
cout << "移动构造" << this << endl;
}
A(const string &s) {
cout << "构造函数 " << this << endl;
str = s;
}
string str;
shared_ptr<B> ptr;
};
A getA() {
A a("aaaaaaa");
return a;
}
A* GetA() {
A* p = new A("bbbbbbbbbbbb");
return p;
}
void main() {
{
A a = getA();
cout << a.str << endl;
}
cout << endl;
cout << endl;
A* b = GetA();
delete b;
}
/**输出**
构造函数 00000016650FF8D8
移动构造00000016650FFAC8
析构函数00000016650FF8D8
aaaaaaa
析构函数00000016650FFAC8
构造函数 000001A81A4983E0
析构函数000001A81A4983E0
*/
这里显示的实现了右值构造函数,在getA()返回时,他会被调用,如果去掉右值构造函数,他会调用拷贝构造函数,但是如果我们没有实现右值构造也没有实现拷贝构造,那他会不会调用默认右值构造函数呢,这个问题知乎上面有人讨论过,我直接在这里记录结论,可能还有待商榷先,先给出链接 C++默认移动构造函数有什么用? - 知乎:
c++ Primer:只有当一个类没有定义任何自己的拷贝控制函数,并且其所有数据成员都可以移动构造或移动赋值时,编译器会为它合成移动构造函数.........................与拷贝操作不同,编译器根本不会为某些类合成移动操作。特别是,如果一个类定义了自己的拷贝构造函数、拷贝赋值运算符或者析构函数,编译器就不会为它合成移动构造函数和移动赋值运算符了。.... ~嗯,这样看来合理,指针所指向的资源不需要析构(也就没定义析构函数),那就默认生成移动构造函数也没问题~
你强制使用std::move那就会被强制调用,那些没有定义移动构造的成员变量的信息应该就会被丢失了。