Стандарты программирования на С++. 101 правило и рекомендация | страница 94



>void Transmogrify(B obj); // Теперь эта функция вообще не

>                          // может быть вызвана (!)


>void Transmogrify2(const B& obj) // Идиома для намерения в

>{                                // любом случае получить

> В b( obj );                     // параметр obj по значению

> // ...                          // (с возможной срезкой)

>}


>B b;              // Базовые классы не должны быть конкретными

>D d;              // (см. рекомендацию 35), но допустим это

>Transmogrify(b);  // Должна быть ошибка (см. примечание)

>Transmogrify(d);  // Должна быть ошибка (см. примечание)

>Transmogrify2(d); // Все в порядке

Примечание: на момент написания данной рекомендации некоторые компиляторы ошибочно допускали один или оба приведенных вызова функции >Transmogrify. Эта идиома вполне стандартна, но (пока что) не полностью переносима.

Имеется лучший способ предупреждения срезки, с более высокой степенью переносимости. Пусть, например, функция наподобие >Transmogrify в действительности хочет получить полную глубокую копию без информации о действительном производном типе переданного объекта. Более общее идиоматическое решение состоит в том, чтобы сделать копирующий конструктор базового класса защищенным (чтобы функция наподобие >Transmogrify не могла случайно его вызвать), а вместо него воспользоваться виртуальной функцией >Clone:

>// добавление функции Clone (уже лучше, но все еще требуется

>// усовершенствование)

>class B { // ...

>public:

> virtual B* Clone() const = 0;

>protected:

> B(const B&);

>};


>class D : public B { // ...

>public:

> virtual D* Clone() const { return new D(*this); }

>protected:

> D( const D& rhs ): B(rhs) {/*...*/ }

>};

Теперь попытка срезки будет (переносимо) генерировать ошибку времени компиляции, а объявление функции >Clone как чисто виртуальной заставляет непосредственный производный класс перекрыть ее. К сожалению, с данным решением все еще связаны две проблемы, которые компилятор не в состоянии обнаружить: в классе, производном от производного, функция >Clone может оказаться неперекрытой, а перекрытие >Clone может реализовать ее некорректно, так что копия будет не того же типа, что и оригинал. Функция >Clone должна следовать шаблону проектирования Nonvirtual Interface (NVI; см. рекомендацию 39), который разделяет открытую и виртуальную природы >Clone и позволяет вам использовать ряд важных проверок:

>class В { // ...

>publiс:

> B* Clone() const { // Невиртуальная функция

>  B* р = DoClone();

>  assert(typeid(*p) == typeid(*this) &&