Я немного не уверен, когда нужен конструктор копирования. Например, с учетом этой функции:

template<class T>
T max(const T* array, int size) {
    T result = array[0];
    for (int i = 1; i < size; ++i) {
        if (result < array[i]) {
            result = array[i];
        }
    }
    return result;
}

По какой причине мне нужен конструктор копирования для типа T? Я думаю, это должно быть потому, что мы возвращаемся по стоимости. Нужен ли для этой строки T result = array[0]; также конструктор копирования?

4
Software_t 14 Мар 2018 в 14:41

2 ответа

Лучший ответ

По какой причине мне нужно copy constructor для типа T?

T result = array[0];

Это называется инициализацией копирования и вызывает конструктор копирования для типа T. Для успешного выполнения этой строки типу T потребуется конструктор копирования.

Я думаю, это должно быть потому, что мы возвращаемся по значению, и поэтому нам нужно copy constructor для типа T.

return result;

По большей части ваше предположение верно для возвращаемого значения. Однако в этом случае определять конструктор копирования не обязательно. Чтобы реализовать семантику перемещения, вы можете реализовать конструктор перемещения, который устранит необходимость в конструкторе копирования , поскольку локальная переменная result будет "перемещена" вместо "скопирована" из. Семантика перемещения устраняет необходимость в ненужных копиях больших объектов при их возврате из функции, поскольку эти большие объекты не будут доступны после возврата из функции.

5
Arnav Borborah 14 Мар 2018 в 15:00

На это уже ответили здесь:

В чем разница между оператором присваивания и конструктором копирования?

Итак, дело в следующем:

Конструктор копирования используется для инициализации ранее неинициализированного объекта из данных какого-либо другого объекта.

Оператор присваивания используется для замены данных ранее инициализированного объекта данными какого-либо другого объекта.

Вот пример:

#include <iostream>

using namespace std;


class MyClass{
public:
  MyClass(){
    cout << "Default ctor\n";
  }

  MyClass(const MyClass& copyArg){
    cout << "Copy ctor\n";
  }

  MyClass(MyClass&& moveArg){
    cout << "Move ctor\n";
  }

  void operator=(const MyClass& assignArg){
    cout << "Assignment operator\n";
  }

  bool operator<(const MyClass& comparsionArg) const {
    return true;
  }
};

template<class T>
T max(const T* array, int size) {
    T result = array[0];
    for (int i = 0; i < size; ++i) {
        if (result < array[i]) {
            result = array[i];
        }
    }
    return result;
}


int main(){

  MyClass arr[1];

  const MyClass& a = max(arr, 1);

  return 0;
}

Чтобы узнать, что именно происходит, нам нужно скомпилировать с помощью -fno-elide-constructors.

Результат:

Default ctor
Copy ctor
Assignment operator
Move ctor

Итак, здесь конструктор по умолчанию вызывается в этой строке для одного элемента массива:

MyClass arr[1];

Затем мы инициализируем ранее неинициализированный объект и вызывается конструктор копирования :

T result = array[0];

Затем мы присваиваем ранее инициализированному объекту и вызываем оператор присваивания :

result = array[i];

После того, как нам нужно создать объект за пределами области действия нашей функции, поскольку мы возвращаемся по значению и для этого конструктора перемещения вызывается:

return result;

Затем привяжите объект, созданный с помощью конструктора перемещения в области main, к ссылке const:

const MyClass& a = max(arr, 1);
1
Toby Speight 14 Мар 2018 в 15:02