Вызов конструктора копии С++

Насколько я знаю, конструктор копирования вызывается в следующих сценариях:

1) Pass by value
2) Return by value
3) When you create and initialize a new object with an existing object

Здесь программа:

#include <iostream> 
using namespace std; 
class Example 
{ 
    public:
        Example() 
        {
            cout << "Default constructor called.\n";
        }
        Example(const Example &ob1) 
        {
            cout << "Copy constructor called.\n";
        }
        Example& operator=(const Example &ob1) 
        {
            cout << "Assignment operator called.\n"; 
            return *this;
        }
        ~Example()
        {
            cout<<"\nDtor invoked"<<endl;
        }
        int aa;
};

Example funct() 
{
    Example ob2;
    ob2.aa=100;
    return ob2;
}



int main() 
{
    Example x;
    cout << "Calling funct..\n";
    x = funct();
    return 0;
}

Вывод:

Вызывается конструктор по умолчанию.

Функция вызова.

Вызывается конструктор по умолчанию.

Вызывается оператор присваивания.

Dtor вызывается

Dtor вызывается

Пожалуйста, исправьте меня, IIRC должна произойти следующая последовательность вызовов:

1) Конструктор x называется

2) Конструктор ob2 называется

3) Функция возвращается и поэтому вызывается конструктор копирования (чтобы скопировать ob2 в неназванную временную переменную i.e funct())

4) Деструктор ob2, называемый

5) Назначьте неназванную временную переменную x

6) Уничтожьте временную переменную i.e вызовите ее деструктор

7) Уничтожьте x i.e вызовите x destructor

Но тогда почему конструктор копирования не вызывается, и также есть только 2 обращения к dtors, тогда как я ожидаю 3.

Я знаю, что компилятор может делать оптимизации, однако, правильно ли я понимаю?

Большое спасибо:)

Привет

Lali

Ответ 1

Конструктор копирования не может быть вызван при возврате по значению. Некоторые компиляторы используют функцию оптимизации возвращаемых значений.

Подробнее о Оптимизация возвращаемого значения "

Ответ 2

Часть стандарта, которая сообщает вам, когда компиляторы могут выдавать копии, составляет 12.8/15. Всегда нужно компилятору, действительно ли выполнять эликсирование. Есть две юридические ситуации, плюс любая их комбинация:

  • "в возвращаемом выражении в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта с тем же самым cv-неквалифицированным типом, что и тип возвращаемого значения функции

  • ", когда временный объект класса, который не был привязан к ссылке, будет скопирован в объект класса с тем же самым cv-неквалифицированным типом".

Первый обычно называется "Именованная оптимизация значений", и это то, что разрешает вывод, который вы видите в своем примере. Последнее по сути превращает инициализацию копирования в прямую инициализацию и может происходить, например, если ваш код сделал Example x = Example();.

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

Example x;
Example y = x;

Но если x в противном случае не использовался, и у cctor не было побочных эффектов, тогда я думаю, что его можно было бы оптимизировать, как и любой другой код, который ничего не делает.

Ответ 3

При выполнении x = funct(); компилятор замечает, что он будет возвращен непосредственно и, таким образом, позволит избежать бесполезной конструкции. Вот почему вы получите только два вызова деструктора.

Вот пример, почему иногда работа с "копией" не обязательно является потерянной.

Ответ 4

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

Ответ 5

g++ v4.4.1 имеет возможность подавлять оптимизацию "elide":

[email protected]$  g++ -fno-elide-constructors Example.cpp -o Example

[email protected]$  ./Example 

Default constructor called.
Calling funct..
Default constructor called.

Copy constructor called.

Dtor invoked
Assignment operator called.

Dtor invoked

Dtor invoked

Как вы видите, теперь конструктор копирования вызывается!