Почему конструктор копирования вызывается, когда мы передаем объект в качестве аргумента по значению методу?

Почему конструктор копирования вызывается, когда я передаю объект в качестве аргумента по значению в функцию? Пожалуйста, посмотрите мой код ниже: я передаю объект класса в качестве аргумента по значению функции display(), но он вызывает конструктор копирования до того, как управление переходит к функции display(). Я не понимаю, почему.

#include "stdafx.h"
#include <iostream>
using namespace std;

class ClassA
{
    private:
       int a, b;
    public:
       ClassA()
       {
           a = 10, b = 20;
       }
       ClassA(ClassA &obj)
       {
           cout << "copy constructor called" << endl;
           a = obj.a;
           b = obj.b;
       }
};
void display(ClassA obj)
{
   cout << "Hello World" << endl;
}
int main()
{
   ClassA obj;
   display(obj);
   return 0;
}

person nagaradderKantesh    schedule 24.05.2013    source источник


Ответы (3)


Чтобы немного уточнить два ответа, которые уже даны:

Когда вы определяете переменные как «такие же, как» некоторые другие переменные, у вас есть две основные возможности:

ClassA aCopy = someOtherA; //copy
ClassA& aRef = someOtherA; //reference

Вместо неконстантных ссылок lvalue, конечно, есть константные ссылки и ссылки rvalue. Главное, что я хочу здесь отметить, это то, что aCopy не зависит от someOtherA, а aRef практически такая же переменная, как someOtherA, это просто другое имя (псевдоним) для нее.

С параметрами функций в основном то же самое. Когда параметр является ссылкой, он привязывается к аргументу при вызове функции и является просто псевдонимом для этого аргумента. Это означает, что вы делаете с параметром, вы делаете с аргументом:

void f(int& iRef) {
  ++iRef;
}

int main() {
  int i = 5;
  f(i); //i becomes 6, because iRef IS i
}

Когда параметр является значением, это всего лишь копия аргумента, поэтому независимо от того, что вы делаете с параметром, аргумент остается неизменным.

void f(int iCopy) {
  ++iCopy;
}

int main() {
  int i = 5;
  f(i); //i remains 5, because iCopy IS NOT i
}

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

Существует руководство от GotW #4. :

Предпочитайте передавать параметр только для чтения с помощью const&, если вы собираетесь только читать его (а не копировать).

person Arne Mertz    schedule 24.05.2013

Поскольку передача функции по значению означает, что у функции есть собственная копия объекта. Для этого вызывается конструктор копирования.

void display(ClassA obj)
{
   // display has its own ClassA object, a copy of the input
   cout << "Hello World" << endl;
}

Имейте в виду, что в некоторых случаях копии могут быть опущены, например, если в функцию передается временное значение.

person juanchopanza    schedule 24.05.2013

Как сказал juanchopanza: вы проходите по значению, что приводит к созданию копии. Если вы хотите предотвратить это, вы можете передать по ссылке:

void display(const ClassA &obj)

На заметке: вы должны объявить свой ctor копии, чтобы принять аргумент как ссылку на константу:

ClassA(const ClassA &obj)

В противном случае вы не сможете использовать copy ctor для именованных объектов, помеченных как const или с временными объектами. Это также предотвращает случайное изменение переданного объекта.

person Tom Moers    schedule 24.05.2013