Стандарты программирования на С++. 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) &&