|
Программирование >> Решение нетривиальных задач
Все сообщения, не меняющие внутреннее состояние объекта, объявлять с модификатором const подобным образом: class cls public: int operator==( const cls &p ) const ; (Это тот модификатор const справа, относительно которого я тут распинаюсь). Этот const говорит компилятору, что передача сообщения объекту, объявленному константным, безопасна. Заметьте, что этот самый правый модификатор const в действительности создает следующее определение для указателя this: const current class *this; Если код в этой константной функции попытается модифицировать любое поле данных класса или предпримет вызов другой функции-члена, не помеченной const, то вы получите сообщение об ошибке компиляции такого примерно содержания не могу преобразовать указатель на const current class в указатель на current class . Упомянутым указателем в данном случае является this, и никогда не будет дозволено преобразование указателя на константу в указатель на переменную (потому что вы тогда могли бы модифицировать константу при помощи указателя). Константные ссылки тоже важны и рассматриваются позже. 114. Используйте структуры только тогда, когда все данные открытые и нет функций-членов Это правило является вариантом принципа если это похоже на Си, то должно и действовать как Си . Используйте структуры, только если вы делаете что-то в стиле Си. Следует также избегать наследования от структуры. Даже если мне многое не удалось изложить четко, надеюсь, что я прояснил смысл тезиса закрытые данные или никакие . Зная о проблемах с прямым доступом к открытым данным, вы можете понять, почему следующее не является очень хорошей идеей: typedef struct tagSIZE Существующее определение из заголовочного файла Си LONG cx; LONG cy; SIZE; class CSize : public SIZE Определение в файле Си++ ... Я видел определения классов, подобные следующему, где требуется доступ к полям cx и cy базового класса через указатель производного класса для того, чтобы определить соответствующее им значение третьей координаты - высоты. Например: CSize some size; some size.cy; тьфу! Вы должны иметь возможность написать: some size.height(); У предшествующего кода есть другая, более трудно уловимая проблема. Наследование от существующей структуры Си часто выполняется программистом, который верит, что сможет передать объект Си++ в существующую функцию Си. То есть программист полагает, что раз наследование фактически добавляет поля к базовому классу, то производный класс в буквальном смысле будет расположен точно так же, как и базовый класс, но с присоединением нескольких дополнительных полей. Однако, это может быть и не так. Если производный класс добавляет, например, виртуальную функцию, то в базовый класс может быть добавлен указатель на таблицу виртуальных функций. Аналогично, если производный класс использует множественное наследование одновременно от структуры Си и чего-то еще, то нет никакой гарантии, что структура Си будет в начале. 115. Не размещайте тела функций в определениях классов Здесь есть несколько проблем. Если вы действительно поместите тело функции в определение класса таким образом: class amanda public: void peekaboo( void ){ cout << ку-ку\п ; } функция игры в прятки с Амандой Си++ делает этот класс встроенным. Первая проблема заключается в том, что такие функции с течением времени имеют тенденцию разрастаться и становятся слишком большими, чтобы быть встроенными. Поэтому лучше помещать определения своих встроенных функций вне определения класса, но в том же заголовочном файле, где размещается определение класса: class amanda public: void peekaboo( void ); class amanda::peekaboo( void ) cout << ку-ку\п ; Путаница - более крупная проблема, чем размер. Часто определение класса является единственной имеющейся у вас определенной документацией по членам класса. Вам на самом деле нужно, чтобы все поместилось на одной странице, и чтобы это определение давало краткий список прототипов функций. Если имена функции и аргумента выбраны точно, то это часто вся документация, которая вам необходима. Как только вы начинаете добавлять тела функций, даже если они состоят из одной строки, к определению класса - вы эту ясность теряете. Определение класса начинает распространяться на несколько страниц, и становится трудно найти что-нибудь, используя определение класса в качестве средства документирования. Третья проблема более коварна и потребует нескольких часов на устранение, если вы не будете аккуратны. Рассмотрим фрагмент реализации связанного списка на листинге 8 (который не будет компилироваться). Классы linked list и list node посылают сообщения друг другу. Компилятор должен увидеть определение класса до того, как он позволит вам послать сообщение объекту этого класса. (Вы можете объявить указатель на объект, лишь глядя на class xxx; но вы не можете ничего сделать при помощи этого указателя до завершения определения всего класса). Так как в листинге 8 используются встроенные функции, то невозможно устроить эти определения классов так, чтобы избежать предварительных ссылок. Вы можете решить эту проблему, поместив определения функций в конце того файла, где они объявлены. Я сделал это в листинге 9.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |