CHAP8_1 (1018808), страница 5

Файл №1018808 CHAP8_1 (Сборник литературы - С и С++) 5 страницаCHAP8_1 (1018808) страница 52017-07-08СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 5)

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

Хорошим примером этой проблемы в С++ является char*. Большинство программистов на С ни за что не соглашаются отказаться от использования строк в виде char*. Проблема заключается в том, что вы привыкли смотреть на char* и думать, что это строка. Это не строка. Это указатель. Убежденность в том, что указатель - это строка, обычно вызывает проблемы, некоторые из которых я уже рассматривал, а другие будут рассмотрены позднее.

Симптомами этой проблемы является появление char* где-нибудь в программе, которая поддерживает класс string; вы должны делать все на языке string. Обобщим это: чтобы заставить объектно-ориентированную систему работать, все должно быть объектами. Основные типы С не очень применимы, за исключением глубоких недр низкоуровневых функций-членов класса низкого уровня. Инкапсуляция вашего char* в классе string решит множество проблем, и потратите массу времени, пытаясь поддерживать char*, при том, что существует вполне хороший класс string, который может делать ту же работу.

Определение класса не обязательно увеличивает накладные расходы, поэтому это не может быть оправданием. Если ваш класс 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 со страницы 111 является виртуальной, то я могу сделать следующее:

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. Функция-член должна обычно использовать закрытые поля данных класса.

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

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

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

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

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

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

    puts( const char *p )

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

  • Все сообщения, не меняющие внутреннее состояние объекта, объявлять с модификатором const подобным образом:

    class cls

    {

    public: int operator==( const cls &p ) const ;

    };

    (Это тот модификатор const справа, относительно которого я тут распинаюсь). Этот const говорит компилятору, что передача сообщения объекту, объявленному константным, безопасна. Заметьте, что этот самый правый модификатор const в действительности создает следующее определение для указателя this:

    const current_class *this;

    Если код в этой константной функции попытается модифицировать любое поле данных класса или предпримет вызов другой функции-члена, не помеченной const, то вы получите сообщение об ошибке компиляции такого примерно содержания "не могу преобразовать указатель на const текущий_класс в указатель на текущий_класс". Упомянутым указателем в данном случае является 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 << "ку-ку\n"; } // функция игры в прятки с Амандой

}

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

class amanda

{

public:

void peekaboo( void );

}

class amanda::peekaboo( void )

{

cout << "ку-ку\n";

}

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

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

Третья проблема более коварна и потребует нескольких часов на устранение, если вы не будете аккуратны. Рассмотрим фрагмент реализации связанного списка на листинге 8 (который не будет компилироваться). Классы linked_list и list_node посылают сообщения друг другу. Компилятор должен увидеть определение класса до того, как он позволит вам послать сообщение объекту этого класса. (Вы можете объявить указатель на объект, лишь глядя на class xxx; но вы не можете ничего сделать при помощи этого указателя до завершения определения всего класса). Так как в листинге 8 используются встроенные функции, то невозможно устроить эти определения классов так, чтобы избежать предварительных ссылок. Вы можете решить эту проблему, поместив определения функций в конце того файла, где они объявлены. Я сделал это на листинге 9.

Листинг 8. Фрагмент реализации связанного списка

  1. class list_node;

  2. class linked_list

  3. {

  4. int number_of_elements_in_list;

  5. list_node *root;

  6. private: // этот раздел содержит сообщения, получаемые

  7. friend class list_node; // только от объектов list_node

  8. void have_removed_an_element(void)

  9. {

  10. --number_of_elements_in_list;

  11. }

  12. public

  13. void remove_this_node( list_node *p )

  14. {

  15. // Следующая строка генерирует ошибку при компиляции,

  16. // так как компилятор не знает, что list_node

  17. // имеет сообщение remove_yourself_from_me( &root ).

  18. p->remove_yourself_from_me( &root );

  19. }

  20. // ...

  21. };

  22. class list_node

  23. {

  24. linked_list *owner;

  25. private: // Этот раздел содержит сообщения,

  26. friend class linked_list: // получаемые только от объектов linked_list

  27. void remove_yourself_from_me( list_node *root )

  28. {

  29. // ... Выполнить удаление

  30. owner->have_removed_an_element();

  31. }

  32. };

Листинг 9. Улучшенный вариант реализации связанного списка

  1. class list_node;

  2. class linked_list

  3. {

  4. int number_of_elements_in_list;

  5. list_node *root;

  6. private:

  7. friend class list_node;

  8. void have_removed_an_element( void );

  9. public

  10. void remove_this_node( list_node *p );

  11. //...

  12. };

  13. //===============================================================

  14. class list_node

  15. {

  16. linked_list *owner;

  17. private: // Этот раздел содержит сообщения,

  18. friend class linked_list: // получаемые только от объектов

  19. // linked_list

  20. void remove_yourself_from_me( list_node *root );

  21. };

  22. //===============================================================

  23. // функции класса linked_list:

  24. //===============================================================

  25. inline void linked_list::remove_this_node( list_node *p )

  26. {

  27. p->remove_yourself_from_me( &root );

  28. }

  29. //--------------------------------------------------------------------------------------------------------------------

  30. inline void linked_list::have_removed_an_element( void )

  31. {

  32. --number_of_elements_in_list;

  33. }

  34. //===============================================================

  35. // функции класса list_node:

  36. //===============================================================

  37. void list_node::remove_yourself_from_me( list_node *root )

  38. {

  39. // ... Выполнить удаление

  40. owner->have_removed_an_element();

  41. }

116. Избегайте перегрузки функций и аргументов, используемых по умолчанию.

Характеристики

Тип файла
Документ
Размер
108,5 Kb
Тип материала
Высшее учебное заведение

Список файлов книги

С и С++ - сборник литературы
C++ Бархатный путь - Марченко А
cpp_001.shtml
cpp_002.shtml
cpp_003.shtml
cpp_004.shtml
cpp_005.shtml
cpp_006.shtml
cpp_007.shtml
cpp_008.shtml
cpp_009.shtml
cpp_010.shtml
cpp_011.shtml
cpp_012.shtml
cpp_013.shtml
cpp_014.shtml
cpp_015.shtml
cpp_016.shtml
cpp_017.shtml
cpp_018.shtml
cpp_019.shtml
cpp_020.shtml
cpp_021.shtml
cpp_022.shtml
cpp_023.shtml
cpp_024.shtml
cpp_025.shtml
cpp_026.shtml
cpp_027.shtml
cpp_030.shtml
cpp_034.shtml
Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
7033
Авторов
на СтудИзбе
260
Средний доход
с одного платного файла
Обучение Подробнее