cplus-plus.ru - Изучение С++ онлайн

понедельник, Сентябрь 07, 2009

Конструктор или оператор присваивания?

В C++ не всегда бывает так, что знак = означает вызов оператора присваивания, из-за чего народ начинает путаться.

Я нашла в comp.lang.c++.moderated большой хороший пример, может пригодится кому.


class B { ... };

class A
{
...
public:
// Constructor
A() { ... }

// Copy constructor
A(const A& a_obj) { ... }

// Constructor overloading
A(const B& b_obj) { ... }
...
A& operator=(const A& a_src) { ... }
...

};

// Примеры:
A a1; // конструктор вида A()
A a2(a1); // конструктор копирования A(a1)
A a3 = a2; // конструктор копирования A(a2)

B b;
A a4(b); // перегруженный конструктор A(b)
A a5 = b; // перегруженный конструктор A(b)

a1 = a5; // operator=(const A&) то есть operator=(a5)
a2 = b; // Шаг1: создается временный объект класса а A
// с помощью конструктора вида A(b),
// Шаг2: -> operator=(const A&) то есть operator=(A(b))

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

15 коммент.:

rikkitikkitavi комментирует...

Я бы еще добавил про последнюю строчку:
там - как и описано Алёной - происходят не очевидные с первого взгляда вещи, что есть не очень хорошо,
в связи с этим, например, Гугл рекомендует конструкторы с одним параметром объявлять как explicit, чтобы такие вещи явно прописывались в коде и не прятали за собой сюрпризов - ведь можно подумать, что скорее всего тут сработатывает оператор копирования

fuwaneko комментирует...

Ну, товарищи знакомые с языками вроде Erlang знают, что оператор присваивания не то, чем кажется :) Точнее, что его вообще может не быть.

Alex комментирует...

Не знаю как кому но меня это в заблуждение никогда не вводило:) ИХМО это такие вещи в которых путатцо нильзя.

wrathchildtoo комментирует...

К слову, конструктор такого типа
A(const B& b_obj) { ... }
называется конструктором преобразования

Анонимный комментирует...

Не совсем корректный пример!!!

// да. конструктор A()
A a1;

// это - вызов explicit
// конструктора A(const A &),
// который есть конструктор
// копирования
// *по совместительству*
A a2(a1);

А то что rikkitikkitavi сказал насчет explicit- так любая декларация объекта- это вызов конструктора либо со знаком "=" либо explicit- со скобочками, либо без всего.

В моем понимании- существует "нативный" контекст конструктора копирования- это вызов, который генерируется компилятором для передачи значений по стеку. Но в контексте декларации- это обычный конструктор.

Roman комментирует...

А как происходит у вас на практике?
То есть стараетесь вы объявлять конструкторы копирования explicit?
Или же за этим четко не следите?

rikkitikkitavi комментирует...

2 Roman: честно говоря, Google C++ Style Guide я прочитал совсем недавно, с тех пор стараюсь так писать, а до этого сам как-то не думал

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

rikkitikkitavi комментирует...

все мои слова относятся в первую очередь к конструкторам "преобразования"

не глядя в объявления классов вы можете сказать сколько объектов класса А будет создано

B b;
A a;
a = b;

?
правильно, не можете
глядя на этот код большинство людей ожидает что выполнится оператор копирования
а теперь представьте, что по каким-то причинам создание временного объекта критично и/или бажно
сколько времени вы потратите на поиск этого

примерно такие доводы у гугла, почитайте

stalkerg комментирует...

В целом очевидные вещи но, если копаешь чужой код то это просто ад. (Вспомниаю кучу таких классов в Вангерах)

Анонимный комментирует...

2 rikkitikkitavi

Большое спасибо за наводку на Google C++ Style Guide :)

Саша комментирует...

Если я ничего не путаю, поведение компилятора в случае со строчкой
A a2 = a1;
зависит от "настроения" компилятора и может интерпретироваться либо как вызов конструктора копирования, либо как вызов конструктора по умолчанию и последующего вызова оператора присваивания.

rikkitikkitavi комментирует...

согласно стандарту, эта строчка должна интерпретироваться компилятором как вызов конструктора копирования

Kodt комментирует...

Чтобы избежать сюрпризов, нужно все конструкторы, операторы приведения типов и операторы присваивания делать согласованными.
Тогда нет особой разницы (кроме просадки производительности), что за цепочка там сработала, да ещё с учётом RVO всяческого.

Либо бить по рукам с помощью explicit или private.

DAloG комментирует...

Присоединяюсь к благодарностям за Google C++ code style))

Дмитрий комментирует...

По-моему, сложнее всего для новичков отличить случай вызова конструктора с аргументом от случая с последовательным вызовом конструктора с аргументом в правой части присваивания и, собственно, оператора присваивания.
P.S. Алене спасибо за пост и за помощь по этому вопросу :) Также присоединяюсь к благодарностям за Google C++ Style Guide.