"Программирование на Visual C++. Архив рассылки" - читать интересную книгу автора (Jenter Алекс)

Программирование на Visual C++ Выпуск №15 от 18 сентября 2000 г.

Доброе время суток!

Сегодня мы с вами поговорим о такой вещи, как стиль программирования.

Легко читаемый код  и надежность программ

Если вам когда-нибудь приходилось разбираться в чужой программе, или даже в своей собственной, но написанной несколько лет назад, да еще когда поджимает время, вы скорее всего знаете, какая это неприятная и сложная задача. Порой бывает легче весь код написать заново (а многие так и делают). Конечно, существует поговорка "настоящие программисты не пишут комментарии: если программу было трудно написать, в ней должно быть трудно что-то понять и еще труднее что-либо изменить". О комментариях мы еще поговорим, печально то, что очень многие следуют этому принципу в самой программе. "Неважно", думают они, "как это написано, главное побыстрее проверить, работает ли это вообще". Самые прилежные из них впоследствии переписывают отдельные куски кода, но таких единицы. Большинство же, убедившись в работоспособности алгоритма, оставляют все как есть и переходят к новым задачам.

Как показывает многолетняя практика, это большая ошибка. Усилия, потраченные на "причесывание" кода, окупаются с лихвой. Но по-настоящему ценится умение писать сразу такой код, который не нуждается в причесывании. Это умение не приходит само по себе, а вырабатывается постепенно, в нем надо постоянно практиковаться. И когда вы говорите себе "а, это ведь просто небольшая утилитка для удобства, вот когда получу настоящий заказ – буду писать красиво", вы тем самым приучаете себя к неправильному стилю написания программ.

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

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

1. Обязательно соблюдайте отступы. Хотя Visual C++ и делает отступы автоматически, иногда они все же нарушаются. С их помощью сразу видна структура программы.

Кстати, многие знают, что для того, чтобы подвинуть блок текста вправо, нужно выделить его и нажать Tab, но почему-то даже не догадываются, что если нажать Shift-Tab, текст сдвинется влево! Попробуйте, это очень удобно. Лучше вместо символа табуляции использовать пробелы (Tools|Options|Tabs|Insert Spaces). Тогда ваши программы в любом редакторе будут с корректными отступами.

2. Про комментарии в коде я ничего говорить не буду… ну, почти ничего. Все, что можно было сказать, уже сказано до меня. Все равно лень людям их писать. Одно только вам посоветую: если уж сильно неохота сочинять комментарии 50/50 с кодом – все-таки постарайтесь самые ключевые и/или неочевидные моменты отмечать.

И запомните: неряшливый и запутанный код нужно не комментировать, а переписывать!

3. Именованные константы пишите в верхнем регистре, чтобы можно было мгновенно отличить их от переменных. Например, MAX_ELEMENTS и BORDER_WIDTH, а не Max_Elements и border_width.

4. Имена переменных начинайте с маленькой буквы, названия типов – с заглавной.

5. Глобальные переменные по написанию должны отличаться от обычных. Как правило, для этого используют префикс "g_": g_RefCount, g_BaseDir. Вообще, их количество следует минимизировать. Статические переменные можно обозначать суффиксом "s_", члены классов- "m_".

6. Переменным, имеющим длительный период существования, следует давать длинные имена. Локальным и временным переменным можно давать имена покороче.

7. Помните, что объект всегда подразумевается, т.е. не нужно повторять имя объекта в его методе. Например, MyObject-gt;GetObjectColor() – эту функцию следует назвать просто GetColor().

8. Вкладывайте смысл в имена функций. Используйте слово "find" когда где-то что-то ищется, "get" когда что-то хотите получить, "set" — установить. "Initialize" или "init" – инициализация, "compute" – вычисление, "open/close" – открытие/закрытие, и т.д.  Также в паре следует использовать следующие имена: add/remove, create/destroy, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, next/previous, old/new, open/close, show/hide. Т.е. если вы одну функцию назвали AddTitle(), то противоположную по действию надо назвать не DestroyTitle() или DeleteTitle(), а RemoveTitle().

9. Перед именами переменных, представляющих количество чего-то, ставьте префикс "n": nColors, nItems. Переменные, обозначающие порядковый номер чего-то, дополняйте суффиком "No": RecordNo, LineNo.

10. Не злоупотребляйте сокращениями. Согласитесь, что, например, смысл GetListAverage() гораздо легче понять, чем GetLstAvg() (ведь это, в принципе, может обозначать и GetLastAvenger() ;-).

11. Избегайте логических переменных, обозначающих отрицание. Found, а не notFound; Good, а не notGood. Вообще, хорошим стилем считается дополнять логические переменные префиксом "is": isFound, isGood. Это же относится к функциям, возвращающим значение true/false, напр. IsKindOf().

12. Константы из типов-перечислений (enum) должны содержать имя типа. COLOR_BLUE, а не BLUE; FILE_ERROR_ALREADY_EXISTS, а не ALREADY_EXISTS.

13. Всегда приводите типы к нужным явно, не полагаясь на автоматику.

14. Переменные, связанные между собой по смыслу, можно объявлять одной строкой:

int x, y, z; 

Record first, last, next, previous;

Никак не связанные переменные лучше объявлять на разных строках, даже если они одного типа.

15. В пустых циклах хорошо явно прописывать continue. Этим вы показываете, что оставили цикл пустым нарочно, а не по ошибке. Пример: while (*p++ = *q++) continue;

Ну, хватит пожалуй. Если кто-то особенно заинтересовался этим вопросом, он может посмотреть более чем 70 подобных правил в Geosoft's C++ Programming Style Guidelines.

Должен заметить, что далеко не со всеми положениями этого документа я согласен. Например, я не считаю нужным обязательно начинать имена функций с маленькой буквы, – действительно важно различать переменные и типы, а функцию от типа отличить гораздо легче. Или еще, например, правило всем private-членам классов давать суффикс "_" — ну вот не нравится и все тут…  Я здесь привел самые, на мой взгляд, нужные и, прошу прощения за тавтологию, "правильные" правила; те, которые встречаются практически во всех документах такого типа.

Так что дам еще один, последний, совет: подходите ко всем правилам, необходимость которых для вас не очевидна, с некоторой толикой здравого смысла.

ВОПРОС-ОТВЕТ

Q. Как в VC++ 6.0 можно сделать окно, которое не будет видно на Taskbar'e?

Kirill

A. Самый простой способ – это создать основное окно с расширеным стилем окна WS_EX_TOOLWINDOW:

hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, szWindowClass, szTitle,  WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, 0, hInstance, NULL);

При использовании MFC следует перекрыть метод PreCreateWindow():

BOOL CMainFrame::PreCreateWindow(CREATESTRUCTamp; cs) {

 if (!CMDIFrameWnd::PreCreateWindow(cs)) return FALSE;

 cs.dwExStyle=WS_EX_TOOLWINDOW;

 return TRUE;

}

Но такое решение не всегда приемлемо – у созданного таким образом окна на тайтлбаре может находится только кнопка закрытия, и его заголовок отличается от заголовков других окон (он меньше). Для того, чтобы исправить эти недостатки, сначала создаем невидимое окно со стилем WS_EX_TOOLWINDOW, а затем дочернее окно, которое будет выполнять роль основного окна приложения. Это будет выглядеть следующим образом:

//…

HWND hWnd1,hWnd2;

hInst = hInstance;

hWnd1 = CreateWindowEx(WS_EX_TOOLWINDOW, szWindowClass1, szTitle, 0, 0, 0, 100, 100, NULL, 0, hInstance, NULL); // cоздаем невидимое окно

// создаем окно, которое будет основным; указываем hWnd1 в кач.родителя:

hWnd2 = CreateWindowEx(0, szWindowClass2, szTitle, WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, hWnd1, 0, hInstance, NULL);

ShowWindow(hWnd1, FALSE); // скрываем 1-ое окно

UpdateWindow(hWnd1);

ShowWindow(hWnd2, nCmdShow); // делаем дочернее окно видимым

UpdateWindow(hWnd2);

Bad Sector

Небольшое дополнение: Если вам нужно убирать кнопку с таскбара только тогда, когда ваше приложение минимизировано (например, чтобы реализовать функцию "минимизировать в системный трей"), то все становится гораздо проще. Достаточно в обработчике OnSysCommand поставить реакцию на минимизацию окна (т.е. когда параметр nID равен SC_MINIMIZE вызывать ShowWindow(hWnd, SW_HIDE)). А при получении соответствующего сообщения от иконки в трее, не забывать восстановить окно. (про работу с системным треем см. выпуск №11).

В ПОИСКАХ ИСТИНЫ

Q. В Visual C++ 6.0  создаётся ImageList с помощью ImageList_LoadImage. Потом две загруженные картинки рисуются в окошке – сначала одна, потом поверх неё другая (используется маска) – функция ImageList_Draw. Проблема в том, что рисуется только в 16 стандартных цветах. Картинка 24-битная. Пробовал и с 256 и 16-цветными, с использованием палитры – эффект тот же. Если не сложно, подскажи, как её нарисовать в 16M цвете (использую только API, без MFC)?

Дрон

Всего вам доброго и не скучайте!

©Алекс Jenter mailto:[email protected] Красноярск, 2000.