我从link学到了移动语义
我上课
class Holder
{
public:
Holder(int size) // Constructor
{
m_data = new int[size];
m_size = size;
}
~Holder() // Destructor
{
delete[] m_data;
}
Holder(const Holder& other)
{
cout << "copy constructor" << endl;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
}
Holder &operator=(const Holder& other)
{
if (this == &other)
return *this;
delete[]m_data;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
return *this;
}
private:
int* m_data;
size_t m_size;
};
此类具有以下复制构造函数:
Holder(const Holder& other)
{
cout << "copy constructor" << endl;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
}
然后移动构造函数已实现为:
Holder(Holder&& other) // <-- rvalue reference in input
{
m_data = other.m_data; // (1)
m_size = other.m_size;
other.m_data = nullptr; // (2)
other.m_size = 0;
}
我有一个问题:为什么我们不实现如下所示的复制构造函数:
Holder( Holder& other)
{
m_data = other.m_data;
m_size = other.m_size;
other.m_data = nullptr;
other.m_size = 0;
}
您能告诉我为什么这种方式不使用吗? 谢谢
答案 0 :(得分:1)
我出于安全考虑。尽管您所描述的内容是允许的(并且当人们知道自己在做什么时是正确的),但这样做可能会导致对象看起来很满,但实际上是空的。
通常,将move构造函数与临时值一起使用时更有用:
Holder a_function(...){...}
然后可以将其用于以下构造:
Holder object(a_function(...));
或者在执行以下操作时避免重新分配/复制大量数据/内存:
Holder object(Holder(100));
但是对于这种情况,特别是没有默认构造函数(因此,通常每个对象在构造之后都应该是满的),方法是将复制构造函数作为您建议的内容(类似于move构造函数),然后执行以下操作:
Holder object1(100);
Holder object2(object1);
最后将以object1
的形式出现,它看起来像是普通对象,但为空。因此,以后可能会成为错误的来源。
尽管很明显,但我应该补充一点,销毁object1不会有问题。只是,如果没有某种类型的安全防护措施(边界检查),则在其生命周期内不使用它会很无聊,这很可能会导致非法内存访问。
答案 1 :(得分:1)
您对“复制构造函数”的实现在语义上介于复制构造函数和move构造函数之间,这会造成混乱,进而带来危险。如果您的意图是始终移动并且永远无法复制对象,则可以使用以下方法将类强制为不可复制:
Holder(const Holder& other) = delete;
Holder& operator=( const Holder& ) = delete;
即使您遵循默认副本构造函数和默认赋值的编译器的非生成规则,明确删除这些方法也更加清楚。
答案 2 :(得分:-1)
我有一个问题:为什么我们不像下面那样实现复制构造函数 :
Holder( Holder& other) { m_data = other.m_data; m_size = other.m_size; other.m_data = nullptr; other.m_size = 0; }
您能告诉我为什么这种方式不使用吗?
以上代码不是将其他状态复制,而是将其他状态/拥有的资源移动到正在创建的当前对象中。
移动构造函数通常会“窃取”参数所占用的资源 (例如,指向动态分配对象的指针,文件描述符,TCP 套接字,I / O流,正在运行的线程等),而不是进行复制 他们,并保留一些有效的论点,否则 不确定状态。
Holder A;
Holder B(std::move(A));
// B is created by calling move constructor
// Resources held by A are transferred to B (ref1)
Holder C;
Holder D(C);
//C is created by calling copy constructor
//state or resources of C, D are same and C can be used after this
//Object C usable