Анимация
JavaScript
|
Главная Библионтека /* виртуальн1й */ mydialog::OnOk( void ) if( data is valid() ) CDialog::OnOk(); Послать сообщение базовому классу else beep(); Обычно содержательное сообщение Windows об ошибке Функция OnOk() является закрытой в производном классе, потому что никто не будет посылать сообщение OnOk() объекту mydialog. OnOk() базового класса не может быть закрытой, потому что вам нужно образовать цепь с ней из замещения производного класса. Вы не желаете, чтобы CDialog::OnOk() была открытой, потому что снова никто не должен посылать сообщение OnOk() объекту CDialog. Поэтому вы делаете ее защищенной. Теперь замещение из производного класса может образовать цепочку с OnOk() , но эта функция не доступна извне. Это не очень удачная мысль - использовать защищенный раздел описания класса для обеспечения секретного интерфейса с базовым классом, которым сможет пользоваться лишь производный класс, потому что это может скрыть отношение сцепления. Хотя подобная защищенная функция иногда единственный выход из ситуации, нормальный открытый интерфейс обычно является лучшей альтернативой. Заметьте, что это правило не имеет обратного действия. Хотя защищенные функции обычно должны быть виртуальными, многие виртуальные функции являются открытыми. 143. Опасайтесь приведения типов (спорные вопросы Си++) Приведение типов в Си рассмотрено ранее, но и в Си++ приведение вызывает проблемы. В Си++ у вас также существует проблема нисходящего приведения - приведения указателя или ссылки на базовый класс к производному классу. Эта проблема обычно появляется при замещениях виртуальных функций, потому что сигнатуры функций производного класса должны точно совпадать с сигнатурами базового класса. Рассмотрим этот код: class base public: virtual int operator==( const base &r)=0; class derived return !p?0: strcmp(key, ((const derived &)r).key )==0; Шаблон функции dynamic cast<t> возвращает 0, если операнд не может быть безопасно преобразован в тип t, иначе он выполняет char *key; public: virtual int operator==( const base &r ) return strcmp(key, ((const derived &)r).key ) ==0; К несчастью, здесь нет гарантии, что передаваемый аргумент r действительно ссылается на объект производного класса. Он не может ссылаться на объект базового класса из-за того, что функция чисто виртуальная: вы не можете создать экземпляр объекта base. Тем не менее, r мог бы быть ссылкой на объект некоего другого класса, унаследованного от base, но не являющегося классом derived. С учетом предыдущего определения следующий код не работает: class other derived : public base int key; ... derived dobj; other derived other; if( derived == other derived ) id be shocked(); Комитет ISO/ANSI по Си++ рекомендовал механизм преобразования типов во время выполнения, который решает эту проблему, но на момент написания этой книги многие компиляторы его не поддерживают. Предложенный синтаксис выглядит подобным образом: class derived : public base char *key; public: virtual int operator==( const base &r ) derived *p = dynamic cast<derived *>( &r ); const some class &operator=( const some class &r ); const some class &operator=( const some class &r ) if( this != &r ) this->~some class(); new( this) some class(r); return *this; Этот вариант оператора new инициализирует указываемый this объект как объект some class, в данном случае из-за аргумента r используя конструктор копии. 12 Некоторые компиляторы в действительности позволяют выполнить явный вызов конструктора, поэтому вы, вероятно, сможете сделать точно так же: преобразование. Это правило является также хорошей демонстрацией того, почему вы не хотите, чтобы все классы в вашей иерархии происходили от общего класса object. Почти невозможно использовать аргументы класса object непосредственно, потому что сам по себе класс object почти лишен функциональности. Вы поймаете себя на том, что постоянно приводите указатели на object к тому типу, который на самом деле имеет переданный аргумент. Это приведение может быть опасным без использования преобразования типов во время выполнения, потому что вы можете преобразовать в неверный тип. Приведение уродливо даже в виде преобразования во время выполнения, добавляя ненужный беспорядок в программу. 144. Не вызывайте конструкторов из операции operator=( ) Хотя это правило говорит о перегруженном присваивании, на самом деле оно посвящено проблеме виртуальных функций. Соблазнительно реализовать operator=() следующим образом: class some class public: virtual ~some class( void ); some class( void ); some class( const some class &r ); 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 [ 65 ] 66 67 68 69 70 71 72 73 74 75 76 |