C ++ copy-construct вопрос о построении и назначении
-
20-09-2019 - |
Вопрос
Вот выдержка из пункта 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
вместо того, чтобы создавать временное.