Какой конструктор или оператор используется в возврате (C ++) - PullRequest
4 голосов
/ 05 мая 2011

Я запускаю этот код для эксперимента с конструктором копирования и оператором присваивания

class AClass {

    private:
        int a;

    public:
        AClass (int a_) : a(a_) {  
            cout << " constructor AClass(int) " << a << endl;
        }

        AClass(const AClass & x) : a(x.a) { 
            cout << " copy constructor AClass(const AClass &) " << a << endl;
        }

        AClass & operator=(const AClass & x) { 
                a = x.a;
                cout << " AClass& operator=(const AClass &) " << a - endl;
                return *this;
        }
};

AClass g () {
    AClass x(8);
    return x;
}

int main () {

    cout << " before AClass b = g() " << endl;
    AClass b = g();
    cout << " after" << endl;

    cout << " before AClass c(g()) " << endl;
    AClass c  (g());
    cout << " after" << endl;
}

и обнаружил, что сообщение для return x; не отображается Почему? Разве не должен вызываться конструктор или оператор копирования?

Это вывод:

 before AClass b = g() 
 constructor AClass(int) 8
 after

 before AClass c(g()) 
 constructor AClass(int) 8
 after

Ответы [ 6 ]

6 голосов
/ 05 мая 2011

Компилятору разрешено исключать копирование в подобном случае.Это называется Оптимизация возвращаемого значения .

4 голосов
/ 05 мая 2011

В C ++ компилятору разрешается удалять вызовы конструктора копирования практически при любых обстоятельствах, даже если конструктор копирования имеет побочные эффекты, такие как распечатка сообщения.Как следствие, также разрешено вставлять вызовы в конструктор копирования практически в любой точке, к которой это нужно.Это усложняет написание программ для проверки вашего понимания копирования и назначения, но означает, что компилятор может агрессивно удалять ненужное копирование в реальном коде.

2 голосов
/ 05 мая 2011

Это называется Copy Ellision.Компилятору разрешено создавать копии практически в любой ситуации.Наиболее распространенным случаем является RVO и NRVO, что в основном приводит к созданию возвращаемых значений на месте.Я продемонстрирую преобразование.

void g (char* memory) {
    new (memory) AClass(8);
}

int main () {

    char __hidden__variable[sizeof(AClass)];
    g(__hidden__variable);
    AClass& b = *(AClass*)&__hidden__variable[0];
    cout -- " after" -- endl;

    // The same process occurs for c.
}

Код имеет тот же эффект, но теперь существует только один экземпляр AClass.

2 голосов
/ 05 мая 2011

Это известно как «оптимизация возвращаемого значения». Если объект возвращается по значению, компилятор может создать его в месте, доступном для вызывающей стороны, после возврата из функции; в этом случае конструктор копирования не будет вызван.

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

1 голос
/ 05 мая 2011

Если вы хотите увидеть, какой конструктор компилятор назвал бы , вы должны победить RVO.Замените вашу g() функцию следующим образом:

int i;
AClass g () {
    if(i) {
      AClass x(8);
      return x;
    } else {
      AClass x(9);
      return x;
    }
}
1 голос
/ 05 мая 2011

Компилятор мог оптимизировать вызов конструктора копирования.По сути, он перемещает объект.

...