Почему конструктор копирования не вызывается? - PullRequest
19 голосов
/ 08 сентября 2010
class MyClass
{
public:
  ~MyClass() {}
  MyClass():x(0), y(0){} //default constructor
  MyClass(int X, int Y):x(X), y(Y){} //user-defined constructor
  MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){} //copy constructor

private:
  int x; int y;
};

int main()
{
  MyClass MyObj(MyClass(1, 2)); //user-defined constructor was called.
  MyClass MyObj2(MyObj); //copy constructor was called.
}

В первом случае, когда MyClass(1, 2) вызывает пользовательский конструктор и возвращает объект, я ожидал, что MyObj вызовет конструктор копирования. Почему не нужно вызывать конструктор копирования для второго экземпляра MyClass?

Ответы [ 4 ]

38 голосов
/ 08 сентября 2010

Всякий раз, когда временный объект создается с единственной целью копирования и последующего уничтожения, компилятору разрешается полностью удалить временный объект и создать результат непосредственно в получателе (то есть непосредственно в объекте, который должен получитькопия).В вашем случае

MyClass MyObj(MyClass(1, 2));

может быть преобразовано в

MyClass MyObj(1, 2);

, даже если конструктор копирования имеет побочные эффекты.

Этот процесс называется elision ofоперация копирования .Это описано в 12.8 / 15 в стандарте языка.

19 голосов
/ 08 сентября 2010

Конструктор копирования может быть исключен в таком случае.

Аналогично с MyClass MyObj = MyClass( 1, 2 );.

И с

std::string str = "hello";

ТакоеВ коде есть неявный вызов конструктора для преобразования char* в std::string.

std::string str = std::string( "hello" ); // same, written more verbosely

Без исключения копирования, «простая» инициализация строки с помощью синтаксиса присваивания приведет к дополнительной глубокой копии.И этот синтаксис на 99% эквивалентен тому, что у вас есть.

5 голосов
/ 08 сентября 2010

Помимо того, что сказал Потатосватер и Андрей Т., обратите внимание, что большинство компиляторов можно уговорить не допускать конструкторов. GCC обычно предоставляет вам -fno-elide-constructors и MSVC с /Od, что должно дать вам желаемый результат. Вот некоторый код:

#include <iostream>

#define LOG() std::cout << __PRETTY_FUNCTION__ << std::endl // change to __FUNCSIG__ on MSVC > 2003

class MyClass
{
public:
  ~MyClass() { LOG(); }
  MyClass():x(0), y(0){LOG(); } //default constructor
  MyClass(int X, int Y):x(X), y(Y){LOG(); } //user-defined constructor
  MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){LOG(); } //copy constructor

private:
int x; int y;
};

int main()
{
 MyClass MyObj(MyClass(1, 2)); //User-defined constructor was called.
 MyClass MyObj2(MyObj); //Copy constructor was called.
}

Скомпилировано с GCC 4.5.0 на MingW32:

 g++ -Wall -pedantic -ansi -pedantic tmp.cpp -o tmp -fno-elide-constructors

Выход:

$ tmp.exe
MyClass::MyClass(int, int)
MyClass::MyClass(const MyClass&)
MyClass::~MyClass()
MyClass::MyClass(const MyClass&)
MyClass::~MyClass()
MyClass::~MyClass()
2 голосов
/ 08 сентября 2010

Что заставляет вас думать, что это не вызвано? Попробуйте это [Редактировать: изменение кода для использования частного копирования ctor, так как доступность должна проверяться, даже если использовать копии ctor, исключенной]:

class MyClass
{
public:
   ~MyClass() {}
   MyClass():x(0), y(0){} //default constructor
   MyClass(int X, int Y):x(X), y(Y){} //user-defined constructor

private:
   MyClass(const MyClass& tempObj):x(tempObj.x), y(tempObj.y){} //copy constructor
   int x; int y;
};

int main()
{
    MyClass MyObj(MyClass(1, 2)); //User-defined constructor was called.
    MyClass MyObj2(MyObj); //Copy constructor was called.
}

Попытка компилировать приводит к ошибкам для обеих строк в main:

myclass.cpp(17) : error C2248: 'MyClass::MyClass' : cannot access private member
 declared in class 'MyClass'
        myclass.cpp(11) : see declaration of 'MyClass::MyClass'
        myclass.cpp(4) : see declaration of 'MyClass'
myclass.cpp(18) : error C2248: 'MyClass::MyClass' : cannot access private member
 declared in class 'MyClass'
        myclass.cpp(11) : see declaration of 'MyClass::MyClass'
        myclass.cpp(4) : see declaration of 'MyClass'

Концептуально, копия ctor используется в обоих случаях, и компилятор обязан проверить, что она доступна. Однако в первом случае компилятор может отказаться от фактического использования копии ctor, если он сможет использовать его.

...