Следующий код печатает только A::A()
, но нет A::A(const A&)
или же operator=
, Зачем?
struct A
{
A() { cout << "A::A()" << endl; }
A(const A& value) { cout << "A::A(const A&)" << endl; }
A& operator=(const A& newValut)
{
cout << "A::operator=" << endl;
return *this;
}
};
A foo()
{
A a; //Ok, there we have to create local object by calling A::A().
return a; //And there we need to copy it, otherwise it will be destroyed
//because it's local object. But we don't.
}
int main()
{
A aa = foo(); //Also there we need to put result to the aa
//by calling A::A(const A&), but we don't.
}
Так что этот код должен напечатать
A::A()
A::A(const A&)
A::A(const A&)
Но это не так. Зачем?
Я полагаю, что нет foo()
под g++
без оптимизации.
Это называется «Оптимизация возвращаемого значения». Компилятору разрешено исключать копию в таком случае.
Вот как в C ++ осуществляется возврат сложного типа: местоположение возвращаемого объекта фактически предоставляется вызывающей стороной до вызова функции, и указатель на этот еще не инициализированный объект передается в качестве скрытого аргумента функции. Функция использует эту ячейку памяти для конструирования возвращаемого объекта из возвращенного выражения.
Поэтому, когда возвращаемый объект должен напрямую инициализировать новый объект, как в вашей программе A aa = foo();
, ему не нужно копировать возвращаемое значение в объект в стеке. Он просит функцию создать объект непосредственно в этом месте. Таким образом, должен быть сделан только один вызов конструктора копирования. (На самом деле, если бы у вас было 2 вызова конструктора копирования, компилятор C ++ не был бы совместимым).
Кроме того, компилятору разрешено оптимизировать этот вызов с помощью оптимизации под названием «Оптимизация возвращаемого значения» или RVO. Как это возможно? Если вы посмотрите на свой код, вы увидите, что вы можете напрямую определить локальную переменную «a» в foo () в предполагаемом месте возвращаемого значения, и, следовательно, не нужно копировать ее снова. Это важная функция, потому что конструктор копирования может быть сложным и медленным, поэтому он может значительно повысить производительность при реализации (и каждый известный мне компилятор реализует эту функцию).
Так что в вашем случае, в зависимости от компилятора, у вас может быть 1 или 0 вызов конструктора копирования, и ваш компилятор все еще совместим.