强制移动语义

9

我正在尝试使用移动语义(仅作为实验)。 以下是我的代码:

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,它只是复制了指针(将指针原封不动地复制了)。如果我删除了复制构造函数,编译器会为MyClass生成一个。

我该如何强制使用移动语义?我该如何使它在这样的构造中被使用:

MyClass mc2(mc); //Move, not copy
-or-
MyClass mc2 = mc; //Move, not copy

我尝试使用“&&”运算符来明确标记右值,但是,当然,它没有起作用。

3个回答

8

您声明 m2 为一个引用,而不是一个值。所以它仍然指向它初始化的内容,即 m1。您想要的是:

MyClass mc2 = std::move(mc);

实时示例

至于第二部分 - 没有办法强制使用这样的结构:

MyClass mc2(mc); //Move, not copy
//-or-
MyClass mc2 = mc; //Move, not copy

移动对象。如果您想要从左值(并且mc确实是左值)移动,则必须明确使用std::move(或另一种强制类型转换为右值)。

有一件事情你可以做,但这是一个肮脏的hack,使代码不直观,并产生大量错误。您可以添加一个以非const引用为参数的复制构造函数(和复制赋值运算符)重载,该函数将进行移动。基本上类似于在std::auto_ptr合理过时之前所做的事情。但是,例如对我来说,它永远无法通过代码审查。如果您想要移动,请只使用std::move


一些注意事项:

  • 在空指针上调用delete或者delete[]保证不会执行任何操作,因此您可以安全地从析构函数中删除if语句。

  • 通常最好在C++代码中使用std::copy而不是memcpy,这样您就不必担心正确设置sizeof了。


哦,现在它可以工作了,谢谢。那第二个问题部分呢?关于构造函数和赋值。 - Xanx
@Xanx 我从问题中假设你已经尝试过 MyClass mc2 = std::move(mc); 为什么你立即得出需要使用 && 的结论呢? - user1508519
@Xanx,我已经进行了编辑,使您可以强制移动,但请不要这样做。这很丑陋。 - Angew is no longer proud of SO
@Angew 谢谢,我现在明白了。实际上我不需要强制它,我只是想检查一下是否可能。 - Xanx

1

如果删除复制构造函数和赋值运算符,您可以强制使用移动语义。

MyClass(const MyClass& src)= delete;
void operator=(const MyClass& src) = delete;

在这种情况下,将选择提供的移动构造函数或移动赋值运算符。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接