Вопрос

Вот выдержка из пункта 56 книги "Ошибки в C ++":

Нередко можно увидеть простую инициализацию объекта Y, написанную любым из трех различных способов, как если бы они были эквивалентны.

Y a( 1066 ); 
Y b = Y(1066);
Y c = 1066;

Фактически, все три из этих инициализации, вероятно, приведут к тому, что будет сгенерирован один и тот же объектный код , но они не эквивалентны.Инициализация a известна как прямая инициализация, и она выполняет именно то, что можно было бы ожидать. Инициализация выполняется посредством прямого вызова Y::Y(int).

Инициализации b и c являются более сложными.На самом деле, они слишком сложные.Это оба варианта копирования инициализации.В случае инициализации b мы запрашиваем создание анонимного временного типа Y, инициализированного значением 1066.Затем мы используем это анонимное временное значение в качестве параметра для копирования конструктор для класса Y для инициализации b.Наконец, мы вызываем деструктор для анонимного временного.

Чтобы проверить это, я провел простой класс с элементом data (программа прилагается в конце), и результаты были удивительными.Похоже, что для случая c объект был создан конструктором копирования, а не так, как предложено в книге.

Кто-нибудь знает, изменился ли языковой стандарт или это просто функция оптимизации компилятора?Я использовал Visual Studio 2008.

Пример кода:

#include <iostream>

class Widget
{
    std::string name;
public:
    // Constructor
    Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
    // Copy constructor
    Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
    // Assignment operator
    Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};

int main(void)
{
    // construct
    Widget a("a");
    // copy construct
    Widget b(a);
    // construct and assign
    Widget c("c"); 
    c = a;
    // copy construct!
    Widget d = a;
    // construct!
    Widget e = "e";
    // construct and assign
    Widget f = Widget("f");

    return 0;
}

Выходной сигнал:

Constructing Widget a

Copy constructing Widget from a

Constructing Widget c
Assigning Widget from a to c

Copy constructing Widget from a

Constructing Widget e

Constructing Widget f
Copy constructing Widget from f

Больше всего меня удивили результаты построения d и e.Чтобы быть точным, я ожидал, что будет создан пустой объект, а затем будет создан объект, которому будет присвоен пустой объект.На практике объекты были созданы конструктором копирования.

Это было полезно?

Решение

Синтаксис

X a = b;

где a и b относятся к типу X, всегда означало построение копии.Какие бы варианты, такие как:

X a = X();

используются, никакого присвоения не происходит и никогда не было.Сконструировать и назначить было бы что-то вроде:

X a;
a = X();

Другие советы

Компилятору разрешено оптимизировать случаи b и c быть таким же , как a.Кроме того, компилятор в любом случае может полностью исключить построение копирования и вызовы оператора присваивания, поэтому все, что вы видите, не обязательно совпадает с разными компиляторами или даже настройками компилятора.

Начиная с C ++ 17, все три из этих являются эквивалент (если только Y::Y(int) является explicit, что просто запретило бы c) из - за того , что часто называют исключение обязательного экземпляра.

Даже Y c = 1066; создает только один Y объект, потому что результат неявного преобразования в Y является prvalue, которое используется для инициализации c вместо того, чтобы создавать временное.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top