· При создании класса следует хорошо продумать его интерфейс – средства работы с классом для тех программ, которые будут его использовать. Интерфейс должен быть интуитивно понятным и включать только методы. Поля данных класса должны быть скрытыми.
· Не следует определять методы типа get/set для всех скрытых полей класса — это все равно, что открыть к ним доступ, только более сложным способом. Поля класса вводятся только для того, чтобы реализовать свойства класса, представленные в его интерфейсе с помощью методов.
· Не нужно расширять интерфейс класса без необходимости, «на всякий случай», поскольку увеличение количества методов ведет к трудности понимания класса пользователем. В идеале интерфейс должен быть полным, то есть предоставлять возможность выполнить любые разумные действия с классом, и минимальным — без дублирования и пересечения возможностей методов.
· В виде методов рекомендуется определять только действия, реализующие свойства класса. Если какое-либо действие можно реализовать, не обращаясь к скрытым полям класса, его нет необходимости описывать как метод; лучше описать его как обычную функцию, поместив ее в общее с классом пространство имен. Если функция выполняет действие, не являющееся свойством класса, но нуждается в доступе к его скрытым полям, ее следует объявить как дружественную. Но в общем случае дружественных функций и классов надо избегать, поскольку главной идеей ООП является минимизация связей между инкапсулированными классами.
· Для увеличения производительности программы наиболее часто вызываемые методы можно объявить как встроенные (inline). В основном это касается коротких методов, тело которых оказывается меньше размера кода, генерируемого для их вызова. Кроме ускорения программы за счет исключения вызовов, это дает возможность компилятору производить более полную оптимизацию. Однако необходимо учитывать, что директива inline носит для компилятора рекомендательный характер.
· Конструкторы и деструкторы делать встраиваемыми не рекомендуется, поскольку в них фактически присутствует код, помещаемый компилятором, размер этого кода может быть весьма значительным (например, в конструкторе производного класса должны быть вызваны конструкторы всех базовых и вложенных классов).
· Перегруженные операции класса должны иметь интуитивно понятный общепринятый смысл (например, не следует заставлять операцию + выполнять что-либо, кроме сложения или добавления).
· Если какая-либо операция перегружена, следует, если возможно, перегрузить и аналогичные операции, например, +, += и ++ (компилятор этого автоматически не сделает). При этом операции должны иметь ту же семантику, что и их стандартные аналоги.
· И конструктор копирования, и операция присваивания, создаваемые по умолчанию, выполняют поэлементное копирование из области-источника в область-приемник. Если объект содержит указатели, это приведет к тому, что после копирования два соответствующих указателя разных объектов будут ссылаться на одну и ту же область памяти. При уничтожении первого из объектов эта память будет освобождена, а повторная попытка освободить ее при уничтожении второго объекта приведет к неопределенному поведению программы. Поэтому для классов, содержащих поля-указатели, следует всегда явно определять конструктор копирования и операцию присваивания, выполняющие выделение памяти под динамические поля объекта.
· Динамическая память, выделенная в конструкторе объекта, должна освобождаться в его деструкторе. Невыполнение этого требования приводит к утечкам памяти. Удаление нулевого указателя безопасно (при этом ничего не происходит), поэтому если конструкторы, конструкторы копирования и операция присваивания написаны правильно, любой указатель либо ссылается на выделенную область памяти, либо равен нулю, и к нему можно применять delete без проверки.
· Разница между конструктором копирования и операцией присваивания заключается в том, что последняя работает в том случае, когда объект-приемник уже существует, поэтому в ней перед выделением динамической памяти следует освободить память занятую ранее. Из этого следует, что при реализации операции присваивания для классов, содержащих поля-указатели, необходимо проводить проверку на самоприсваивание и в этом случае оставить объект без изменений. Необходимо также помнить о том, что операция присваивания должна возвращать ссылку на константу.
· В конструкторах для задания начальных значений полям рекомендуется использовать инициализацию, а не присваивание. Инициализация более универсальна, так как может применяться в тех случаях, когда присваиванием пользоваться нельзя (например, при задании значений константным полям или ссылкам). Кроме того, она выполняется более эффективно, потому что создание объекта в C++ начинается с инициализации его полей конструктором по умолчанию, после чего выполняется вызываемый конструктор. Необходимо учитывать и тот факт, что поля инициализируются в порядке их объявления, а не в порядке появления в списке инициализации. Поэтому для уменьшения числа возможных ошибок порядок указания полей в списке инициализации конструктора должен соответствовать порядку их объявления в классе.
· Статические поля не должны инициализироваться в конструкторе, поскольку им нужно присваивать начальное значение только один раз для каждого класса, а конструктор выполняется для каждого объекта класса. Статические поля инициализируются в глобальной области определения (вне любой функции).
· Конструкторы копирования также должны использовать списки инициализации полей, поскольку иначе для базовых классов и вложенных объектов будут вызваны конструкторы по умолчанию.
· Операция присваивания не наследуется, поэтому она должна быть определена в производных классах. При этом из нее следует явным образом вызывать соответствующую операцию базового класса
· Открытое наследование класса Y из класса X означает, что Y представляет собой разновидность класса X, то есть более конкретную, частную концепцию. Базовый класс X является более общим понятием, чем Y. Везде, где можно использовать X, можно использовать и Y, но не наоборот (вспомните, что на место ссылок на базовый класс можно передавать ссылку на любой из производных). Необходимо помнить, что во время выполнения программы не существует иерархии классов и передачи сообщений объектам базового класса из производных — есть только конкретные объекты классов, поля которых формируются на основе иерархии на этапе компиляции.
· Методы, которые должны иметь все производные классы, но которые не могут быть реализованы на уровне базового класса, должны быть виртуальными. Например, все объекты иерархии должны уметь выводить информацию о себе. Поскольку она хранится в различных полях производных классов, эту функцию нельзя реализовать в базовом классе. Естественно назвать ее во всех классах одинаково и объявить как виртуальную с тем, чтобы другие методы базового класса могли вызывать ее в зависимости от фактического типа объекта, с которым они работают. По этой причине деструкторы объявляются как виртуальные.
· При переопределении виртуальных методов нельзя изменять наследуемое значение аргумента по умолчанию, поскольку по правилам C++ оно определяется типом указателя, а не фактическим типом объекта, вызвавшего метод.
6. Среда программирования Visual C++ 6.0
Общий вид окна
Проект (project) – это набор файлов, которые совместно используются для создания одной программы.
Рабочее пространство (workspace) может включать в себя несколько проектов.
После запуска VC++ 6.0 на экране появится окно (рис. 1).
Окно содержит:
· Главное меню (1) – список основных команд VC++;
· Панель инструментов (2) - панель с кнопками команд Visual C++;
· Панель рабочего пространства Workspace (3) - содержит две вкладки:
- ClassView – отображает список классов в проекте,
- FileView – отображает список файлов, входящих в проект.
· Окно для редактирования кодов (4) – окно с текстом программы;
· Выходную панель результатов компиляции (5) - окно для вывода сообщений в процессе компиляции или отладки, показывает текущую стадию компиляции, список ошибок и предупреждений и их количество.
Рис. 1. Окно VC++ 6.0.
Создание консольного приложения и работа с ним
Консольное приложение – это приложение, которое с точки зрения программиста является программой DOS, но может использовать всю доступную оперативную память (если каждый элемент данных программы не будет превышать 1 Мб). Этот тип приложения запускается в особом окне, которое называется “Окно MS-DOS”. На примере консольных приложений прослеживаются этапы развития VC++ при переходе от одной версии к другой.
Каждое приложение, разрабатываемое как отдельный проект в среде VC++6.0, нуждается в том, чтобы ему соответствовало свое собственное рабочее пространство. Рабочее пространство включает в себя те папки, в которых будут храниться файлы, содержащие информацию о конфигурации проекта. Для того чтобы создать новое пространство для проекта, надо выполнить следующие действия:
1. В линейке меню нажать на меню File.
2. Выбрать пункт New или нажать Ctrl+N.
3. Появится окно New. В нем содержится четыре вкладки: Files, Projects, Workspaces, Other Documents. Выбрать вкладку Projects.
4. Из списка возможных проектов выбрать Win32 Console Application для создания приложения DOS.
5. В поле Project name ввести имя проекта.
6. В поле Location ввести путь для размещения каталога проекта, или, нажав на кнопку справа […], выбрать нужную директорию.
7. Должен быть установлен флажок Create New Workspace. Тогда будет создано новое рабочее окно. Нажать кнопку OK
8. Установить один из флажков:
- An empty project – создается пустой проект, не содержащий заготовок для файлов;
- A simple application – создается простейшая заготовка, состоящая из заголовочного файла StdAfx.h, файла StdAfx.cpp и файла реализации;
- A “Hello World” application и An application that supports MFC являются демонстрационными и разными способами демонстрируют вывод на экран строки символов.
Нажать кнопку Finish. Появитсяинформация о созданном проекте содержащая: тип проекта, некоторые особенности и директорию.
После создания проекта в него необходимо записать код программы. При этом можно создать новый файл или добавить в проект существующий файл.
Для создания нового файла надо выполнить следующие действия:
- Выбрать меню File > New или Project > Add to Project > New.
- Открыть вкладку Files.
- Выбрать C++ Source File.
- Чтобы создаваемый файл был автоматически присоединен к проекту, необходимо установить флаг Add to project.
- В поле Filename ввести имя файла.
- В поле Location указать путь для создания файла.
- Нажать OK.
Для добавления существующего файла надо:
1. Выбрать в меню File > Add to Project > Files
2. Указать полное имя файла, который нужно присоединить
Для открытия существующего проекта надо:
1. Выбрать меню File > Open Workspace
2. Указать файл с расширением .dsw
Для сохранения текущего проекта надо выбрать в главном меню File > Save Workspace.
Для закрытия текущего проекта надо выбрать в главном меню File > Close Workspace.
После создания или открытия проекта в окне Workspace появится или список классов, или список файлов входящих в проект. В зависимости от типа проекта, он будет или пустой, или содержать изначально некоторые файлы, присущие данному типу. Проект приложения для DOS изначально пустой. В него можно добавить новые файлы или присоединить уже существующие.