Анимация
JavaScript


Главная  Библионтека 

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

Определение класса не обязательно увеличивает накладные расходы, поэтому это не может быть оправданием. Если ваш класс string имеет единственное поле char*, и если все из методов являются встроенными функциями, то ваши накладные расходы не превысят те, которые бы у вас были при прямом использовании char*, но зато вы получите все выгоды сопровождения, предоставляемые классами Си++. Более того, у вас будет возможность наследовать от string, что невозможно с char*.

Возьмем в качестве примера управляющий элемент-редактор Windows

- маленькое окно, в котором пользователь вводит данные. (Программисты для X-Window, для вас "управляющий элемент" Windows

- это примерный эквивалент widget). Управляющий элемент-редактор имеет все свойства как окна, так и строки, и, следовательно, вам было бы желательно его реализовать, наследуя одновременно от класса window и от класса string.

112. Проектируйте с учетом наследования

Никогда не надейтесь, что класс не будет использоваться в качестве базового класса. Сосредоточимся на случае с примером управляющего элемента-редактора из предыдущего правила. Я бы хотел реализовать такой элемент, наследуя одновременно от класса window и от класса string, потому что он обладает свойствами обоих. У меня ничего бы не получилось, если бы многие из функций string не были виртуальными. То есть, так как я могу делать со строкой следующее:

string str = "xxx"; инициализировать строку значением "xxx" str="Абв"; заменить предыдущее значение на "Абв"

str += "где"; присоединяет "где" к имеющеЙся строке.

то хотел иметь возможность делать следующее, чтобы поместить текст как в буфер, принадлежащий управляющему элементу-редактору, так и в соответствующее окно:

class edit control : public string

, public window {/* ... */}

edit control edit = "xxx";

edit = " Абв";

edit += "где";

Я бы также хотел передавать свой объект edit control в функцию, ожидающую в качестве аргумента string, так чтобы любые изменения, которые эта функция делает в (том, что она принимает за) string, автоматически отображались и в окне управляющего элемента-редактора.



Все это не возможно, если функции, подобные operator=() и operator+=(), не виртуальные в классе string и, тем самым, не позволяющие мне менять их поведение в производном классе edit control. Например, так как функция operator=() класса string из листинга 7 со страницы 155 является виртуальной, то я могу сделать следующее:

class edit control : public string , public window

...

virtual string &operator=( const string &r );

virtual string &edit control:: operator=( const string &r )

*(string *)this = r;

window::caption() = r; операция разрешения видимости

window:: просто для ясности

Следующей функции может быть передан или простой объект string, или объект edit control; она не знает или ей все равно, какой конкретно:

f( string *s )

...

*s="Новое значение";

В случае объекта string внутренний буфер обновляется. В случае edit control буфер обновляется, но также модифицируется заголовок его окна.

112.1. Функция-член должна обычно использовать закрытые поля данных класса

Так как все открытые функции-члены являются обработчиками сообщений, а все закрытые функции и поля данных просто поддерживают открытых обработчиков сообщений, то где-то есть ошибка, если функция не имеет доступа к полям данных или не может вызвать функцию, имеющую к ним доступ. Эта функция должна, вероятно, быть передвинута на глобальный уровень или в другой класс.

Ясным признаком того, что вы сделали что-то неправильно, является функция из одного класса, требующая для своей работы доступа к полям объекта другого класса (в отличие от того, чтобы иметь указатель на



9 Пользователи MFC могут обратиться за более глубоким обсуждением этого вопроса к моей статье "Rewriting the MFC Scribble Program Using an Object-Oriented Design Approach" в августовском номере рнала "Microsoft Systems Journal" за 1995

другой объект для передачи этому объекту сообщения). В самом худшем случае класс "хозяин" дает статус дружественного классу "гость", и функция-член класса "гость" использует указатель "хозяина" для доступа к его полям, но не может получить никакого доступа к любому из полей своего собственного класса. Механизм дружественности часто неверно используется таким способом, но класс должен давать статус друга только так, чтобы друг мог посылать закрытые сообщения классу, дарящему дружбу. Дружественный класс никогда не должен иметь доступ к данным другого класса; это сцепление слишком сильное.

Вы часто видите эту ошибку в архитектурах "документ/отображение" типа MacApp и MFC. С точки зрения архитектуры, "документ" содержит данные, а "отображение" реализует пользовательский интерфейс. Трудности возникают, когда вы хотите показать какие-нибудь данные в своем "отображении". Никогда не позволяйте "отображению" доступ к полям "документа" для их показа. Данные любого класса, включая "документ", должны быть тщательно охраняемым секретом. Лучшим подходом является передача "отображением" в "документ" сообщения "отобразить себя в этом окне".9

113. Используйте константы

В программы на Си класс памяти const часто не включается. На самом деле это просто небрежность, но она мало влияет на возможности программы на Си. Так как Си++ гораздо разборчивее в отношении типов, чем Си, то в Си++ это гораздо более крупная проблема. Вы должны использовать модификатор const везде, где можно; это делает код более надежным, и часто компилятор не принимает код, который его не использует. Особенно важно:

• Всегда передавать указатели на константные объекты, если вы не модифицируете эти объекты. Объявление:

puts( const char *p )

сообщает компилятору, что функция puts() не намерена модифицировать символы в массиве, переданном при помощи p. Это является чрезвычайно полезной порцией информации для сопровождения.



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