【问题标题】:Why is move constructor used over copy constructor?为什么使用移动构造函数而不是复制构造函数?
【发布时间】:2018-11-14 19:12:21
【问题描述】:

这是一个简单的程序,它应该处理一个动态的数字数组,然后过滤掉偶数元素并将它们放入一个新数组中,然后在屏幕上打印这两个数组。

这是头文件:

#pragma once


namespace filter
{
    class Array
    {
        double *arr;
        int n;

public:
    Array();
    Array(int);
    Array(const Array&);
    Array(Array&&);
    void PutIn();
    void PrintOut() const;
    Array isEven();
    Array filter(const std::function<bool(int)>&) const;
    ~Array();

};
}

那么,这是函数的实现:

#include <iostream>
#include <functional>
#include "Array.h";
using namespace filter;
using namespace std;

Array::Array() :arr(nullptr), n(0)
{ }

Array::Array(int n)
{
    this->n = n;
    arr = new double[n];
}

Array::Array(const Array &a1)
{
    n = a1.n;
    arr = new double[n];
    for (int i = 0; i < n; i++)
        arr[i] = a1.arr[i];
}

Array::Array(Array &&a1)
{
    n = a1.n;
    for (int i = 0; i < n; i++)
        arr[i] = a1.arr[i];
    a1.n = 0;
    a1.arr = nullptr;
}

void Array::PutIn()
{
    cout << "Insert elements:\n";
        for (int i = 0; i < n; i++)
            cin >> arr[i];

}
void Array::PrintOut() const
{
    cout << "\nYour array is :\n";
    for (int i = 0; i < n; i++)
        cout << arr[i] << "\t";
}

Array Array::isEven()
{
    return filter([](int x) { return x % 2; });
}
Array Array::filter(const std::function<bool(int)> &f) const
{
    int b = 0;

    for (int i = 0; i < n; i++)
        if (f(arr[i]) == 0)
            b++;

    Array temp(b);
    b = 0;
    for (int i = 0; i < n; i++)
        if (f(arr[i]) == 0)
        {
            temp.arr[b] = arr[i];
            b++;
        }
    return temp;
}

Array::~Array()
{
    delete[]arr;
    n = 0;
}

最后,这是源代码:

#include <iostream>
#include <functional>
#include "Array.h"
using namespace filter;
using namespace std;




int main()
{

    Array a(5);

    a.PutIn();

    Array b = a.isEven();    //WHY THIS LINE OF CODE INVOKES MOVE CONSTRUCTOR AND NOT COPY CONSTRUCTOR?

    a.PrintOut();
    b.PrintOut();

    getchar();
    getchar();

}

所以,如您所见,这是一个相对简单的程序,它需要处理用户输入的包含五个元素的数组,然后创建一个由第一个数组的偶数元素组成的新数组。当我运行它时,它工作正常,但是,这里有一点我不明白。

如果您查看源代码,请注意我留下评论的那一行,即调用 move 构造函数的那一行,但我不知道为什么。这意味着 a.IsEven() 是一个 RVALUE,因为移动构造函数与 RVALUES 一起使用,对吗?谁能解释我为什么这是右值以及理解它的正确方法是什么?任何帮助表示赞赏!

【问题讨论】:

  • isEven 按值返回 Array,这是一个临时的,因此它肯定会调用 b 的移动构造而不是副本。
  • 让移动构造函数分配一个新数组有什么意义?关键是,鉴于源对象可以“空”,您可以窃取他的指针...目前您的实现复制然后泄漏源对象内存。
  • 根据rule of 3/5/0,您将需要一个赋值运算符。请注意,您的移动构造函数不仅不会移动,还会泄漏。
  • 我不认为这段代码“相对简单”。其实我觉得这个比较复杂。手动内存管理绝非易事。只是说...
  • 除非这是“不要以 c++ 方式执行”任务之一,否则您应该使用 std::array

标签: c++ oop


【解决方案1】:

您假设调用isEven 调用您的移动构造函数实际上是不正确的。它也不会调用您的复制构造函数。相反,RVO 确保返回的对象直接在调用站点构造,因此两者都不需要。

Live Demo(它没有解决 cmets 中提到的代码中的任何缺陷)。

【讨论】:

  • 你是在暗示我什至不需要移动和复制构造函数吗?我想我不明白。
  • 您可能需要它们,但在绝大多数情况下不要按值返回Arrays。
  • 我明白了,我以为它被调用了,因为当我调试它时,它看起来好像调用了移动构造函数。
  • 不是真的 - 阅读我提供的“RVO”(又名复制省略)链接的第一段 - 复制省略是按值返回对象时的重要优化,我很惊讶其他评论者没有别提了。
  • 你说得对,MSVC 在isEven 的主体中调用了移动构造函数。我将此称为 MSVC 错误 - 没有必要这样做。
猜你喜欢
  • 2015-06-10
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2021-06-29
相关资源
最近更新 更多