Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 36
Сгенерированный компилятором оператор присваивания для класса Named-Object
Например, предположим, что класс NamedObject определен, как показано ниже. Обратите внимание, что nameValue – ссылка на string, а objectValue имеет тип const T:
>template
>class NamedObject {
>public:
>// этот конструктор более не принимает const name, поскольку nameValue –
>// теперь ссылка на неконстантную строку. Конструктор с аргументом типа
>// char* исключен, поскольку нам нужна строка, на которую можно сослаться
>NamedObject(std::string& name, const T& value);
>... // как и ранее, предполагаем,
>// что operator= не объявлен
>private:
>std::string& nameValue; // теперь это ссылка
>const T objectValue; // теперь const
>};
Посмотрим, что произойдет в приведенном ниже коде:
>std::string newDog(“Persephone”);
>std::string oldDog(“Satch”);
>NamedObject
>// наша собака Персефона собиралась
>// встретить свой второй день рождения
>NamedObject
>// детства) было бы теперь 36 лет
>p = s; // Что должно произойти
>// с данными-членами p?
Перед присваиванием и p.nameValue, и s.nameValue ссылались на объекты string, хотя и на разные. Что должно произойти с членом p.nameValue в результате присваивания? Должен ли он ссылаться на ту же строку, что и s.nameValue, то есть должна ли модифицироваться ссылка? Если да, это подрывает основы, потому что C++ не позволяет изменить объект, на который указывает ссылка. Но, быть может, должна модифицироваться строка, на которую ссылается член p.nameValue, и тогда будут затронуты другие объекты, содержащие указатели или ссылки на эту строку, хотя они и не участвовали непосредственно в присваивании? Это ли должен делать сгенерированный компилятором оператор присваивания?
Сталкиваясь с подобной головоломкой, C++ просто отказывается компилировать этот код. Если вы хотите поддерживать присваивание в классе, включающем в себя член-ссылку, то должны определить оператор присваивания самостоятельно. Аналогичным образом компилятор ведет себя с классами, содержащими константные члены (такие как objectValue во втором варианте класса NamedObject выше). Модифицировать константные члены запрещено, поэтому компилятор не знает, как поступать при неявной генерации оператора присваивания. Кроме того, компилятор не станет неявно генерировать оператор присваивания в производном классе, если в его базовом объявлен закрытый оператор присваивания. И наконец, предполагается, что сгенерированные компилятором операторы присваивания для производных классов должны обрабатывать части базовых классов (см. правило 12), но при этом они конечно же не могут вызывать функции-члены, доступ к которым для них запрещен.