我正在尝试使用移动语义(就像一个实验)。 这是我的代码:
class MyClass {
public:
MyClass(size_t c): count(c) {
data = new int[count];
}
MyClass( MyClass&& src) : count(src.count) {
data = src.data;
src.count = 0;
src.data = nullptr;
}
void operator=( MyClass&& src) {
data = src.data;
count = src.count;
src.count = 0;
src.data = nullptr;
}
~MyClass() {
if (data != nullptr)
delete[] data;
}
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
private:
MyClass(const MyClass& src) : count(src.count) {
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
void operator=(const MyClass& src) {
count = src.count;
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
int* data;
size_t count;
};
int main()
{
MyClass mc(150);
for (size_t i = 0; i < mc.get_count(); ++i)
mc.get_data()[i] = i;
MyClass &&mc2 = std::move(mc);
return 0;
}
但是std :: move不会将mc移动到mc2,它只是复制(copyies指针原样)。如果我删除复制构造函数编译器为MyClass生成它。
如何强制使用移动语义?如何使它在这种结构中使用:
MyClass mc2(mc); //Move, not copy
-or-
MyClass mc2 = mc; //Move, not copy
我试图使用'&amp;&amp;'运算符明确标记rvalue,但是,原因,它不起作用。
答案 0 :(得分:6)
您将m2
声明为引用,不作为值。所以它仍然指的是初始化的内容,即m1
。你想要这个:
MyClass mc2 = std::move(mc);
至于第二部分 - 没有办法强迫这样的结构:
MyClass mc2(mc); //Move, not copy
//-or-
MyClass mc2 = mc; //Move, not copy
移动。如果你想从左值移动(并且mc
确实是左值),你必须明确地使用std::move
(或另一个强制转换为右值)。
你可以做一件事,但这将是一个肮脏的黑客,使代码不直观,并成为错误的一个很好的来源。您可以使用非const引用添加复制构造函数(和复制赋值运算符)的重载,这将执行此操作。基本上像std::auto_ptr
这样的东西在它被合理地弃用之前就已经做过了。但它永远不会通过我的代码审查,例如。如果您想移动,只需std::move
。
一些附注:
在空指针上调用delete
或delete[]
保证是无操作,因此您可以安全地从析构函数中删除if
。
通常最好在C ++代码中使用std::copy
代替memcpy
,您不必担心sizeof
正确
答案 1 :(得分:1)
如果删除复制构造函数和赋值运算符
,则可以强制移动语义MyClass(const MyClass& src)= delete;
void operator=(const MyClass& src) = delete;
在这种情况下,将选择提供的移动构造函数或移动赋值运算符。
答案 2 :(得分:0)
通过一些评论重写一下你的课程。仔细看看,你可能会注意到一些错过的东西。像:
MyClass(size_t c)
中未检查c != 0
。void operator=(const MyClass& src)
而非delete[] data;
(如果存在)。还有一些其他小细节。希望你的编译器可以处理这个问题。
class MyClass {
private:
// initialize memebers directly
int* data = nullptr;
size_t count = 0;
public:
// default empty contructor
MyClass() = default;
// destructor
~MyClass() {
*this = nullptr; // use operator = (nullptr_t)
}
// allow nullptr construct
MyClass(nullptr_t):MyClass() {}
// allow nullptr assignment (for clearing)
MyClass& operator = (nullptr_t) {
if(data) {
delete[] data;
data = nullptr;
}
count = 0;
return *this;
}
// chain to default constructor, redundant in this case
MyClass(size_t c):MyClass() {
// maybe size_t is 0?
if(count = c) {
data = new int[count];
}
}
// chain to default constructor, redundant in this case
MyClass(MyClass&& src):MyClass() {
*this = std::move(src); // forward to move assignment
}
MyClass& operator=(MyClass&& src) {
// don't swap with self
if(&src != this) {
// it's better to swap and let src destroy when it feels like it.
// I always write move contructor and assignment to swap data.
// it's gonna be destroyed anyway, or not...
std::swap(src.data, data);
std::swap(src.count, count);
}
return *this;
}
MyClass(const MyClass& src):MyClass() {
*this = src; // forward to copy assignment
}
MyClass& operator = (const MyClass& src) {
// don't copy to self
if(&src != this) {
// delete first
if(data) {
delete[] data;
data = nullptr;
}
// now reallocate
if(count = src.count) {
data = new int[count];
memcpy(data, src.data, sizeof(int)* count);
}
}
return *this;
}
// easy way to use the object in a if(object) to test if it has content
explicit operator bool() const {
return data && count;
}
// same as above but made for if(!object) to test if empty
bool operator !() const {
return !data || !count;
}
public:
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
// add more custom methods
};
现在移动你做到这一点:
MyClass object1; // default construct
MyClass object1(5); // construct with capacity
MyClass object2(object1); // copy constructor
MyClass object3(std::move(object1)); // move constructor
object2 = object1; // copy assignment
object3 = std::move(object1); // move constructor
std::swap(object2, object3); // swap the two
object2 = nullptr; // to empty it
if(object1); // bool cast