- •1. Дополнения к с
- •1.1. Комментарии
- •1.2. Ключевые слова
- •1.3. Константы
- •1.4. Блочные объявления
- •1.5. Ссылки
- •1.6. Новая роль имён перечислений, структур и объединений
- •1.7. Распределение памяти
- •1.8. Встраиваемые функции
- •1.9. Перегрузка функций
- •1.10. Задание для параметров функции значений по умолчанию
- •1.11. Дополнительные операции для доступа к данным
- •1.12. Предопределённые потоки ввода-вывода
- •2.1. Инкапсуляция
- •2.2. Разграничение доступа (скрытие данных и методов)
- •2.3. Друзья классов
- •2.4. Конструкторы и деструкторы
- •2.5. Конструктор по умолчанию
- •2.6. Конструктор копирования
- •2.7. Несколько слов о деструкторах
- •2.8. Перегрузка операций
- •3.1. Наследование
- •3.2. Виртуальные функции –полиморфизм
- •3.3. Шаблоны
2.5. Конструктор по умолчанию
Конструктор по умолчанию не имеет аргументов и используется при создании неинициализированного объекта:
class MyClass {
public:
MyClass ( ) { }
. . .
// Остальные данные и методы
};
Без такого конструктора объекты не могли бы быть определены без задания инициализирующих значений. Если конструктор по умолчанию не объявлен явно, то компилятор автоматически назначает его. Однако, как и всё остальное, лучше делать это самому. Он вызывается при таких определениях, как
MyClass Obj;
Как и любая другая функция, конструкторы могут иметь аргументы со значениями, заданными по умолчанию. При этом следует избегать неоднозначностей. Например, в случае определения класса
class MyClass {
public:
MyClass ( );
MyClass (int nArg = 0);
. . .
// Остальные данные и методы
};
может возникнуть неопределённость при вызове конструктора:
MyClass clObj1 (7); // Можно. Однозначно вызывается MyClass :: MyClass(int)
MyClass clObj2; // Нельзя. Невозможно выбрать между MyClass :: MyClass ( )
// и MyClass :: MyClass(int nArg = 0)
2.6. Конструктор копирования
Аргументом конструктора может быть и другой объект того же класса. Такой конструктор будет иметь следующий прототип:
MyClass :: MyClass ( MyClass &);
Такие конструкторы запускаются при копировании данных «старого» объекта во вновь создаваемый:
MyClass clObj1(7);
MyClass clObj2 = clObj1;
Если вы не определили для класса конструктор копирования, то компилятор сделает это за вас. Однако, как обычно, лучше это делать самому.
2.7. Несколько слов о деструкторах
В отличие от конструкторов, деструктор класса не имеет аргументов и не может быть перегружен. Как было наглядно продемонстрировано в примере 2.2, деструкторы вызываются строго в обратной последовательности вызова соответствующих конструкторов. Они вызываются автоматически при выходе объекта из блока, в котором были определены. Единственным исключением из этого общего правила является случай, когда объект создается динамически из «кучи» путем вызова оператора new. В этом случае для корректного удаления объекта необходимо явно выполнить оператор delete, который и вызовет необходимый деструктор.
Можно подвести предварительные итоги. Понятие класса С++ устанавливает чётко определённый интерфейс, который помогает разрабатывать, выполнять, поддерживать и сопровождать программы. Концепция класса органично связана с идеей абстракции данных, при которой данные не связываются ни с каким физическим воплощением, а скорее определяются в терминах методов (функций), выполняемых над ними. С этой точки зрения данные и методы рассматриваются как равные, независимые партнёры.
2.8. Перегрузка операций
Так же, как и для функций, язык С++ позволяет переопределить действие большинства операций, чтобы при использовании с объектами конкретного класса они выполняли заданные функции. Такая перегрузка желательна с точки зрения непротиворечивости использования. Классическим примером, приводимым практически во всех учебниках по С++, является выполнение арифметических операций над классами, представляющими комплексные числа. В С++ механизм перегрузки операций реализован путем определения функции с ключевым словом operator, стоящим перед перегружаемой операцией.
Переопределить операцию для объектов класса можно, используя либо соответствующий метод, либо дружественную функцию класса. В случае переопределения операции с помощью метода класса, последний в качестве неявного параметра принимает ключевое слово this, являющееся указателем на данный объект класса. Поэтому если переопределяется бинарная операция, то переопределяющий его метод класса должен принимать только один параметр, а если унарная, то метод класса вообще не имеет параметров.
Если операция переопределяется при помощи дружественной функции, то она должна принимать, соответственно, два и один параметр.
При переопределении операций необходимо учитывать некоторые имеющиеся в языке ограничения:
– нельзя изменять количество параметров операции;
– нельзя изменить приоритет операций;
– нельзя определить новые операции;
– нельзя использовать параметры по умолчанию;
– нельзя переопределять операции ., .*, ->*, ::, ?:, а также символы, обрабатываемые препроцессором;
– синтаксис не позволяет различать операции пре- и постинкремента и декремента.
Рассмотрим небольшой класс CPoint из библиотеки классов MFC (этот класс поддерживает структуру POINT, хранящую текущие координаты. x, y для рисования).
class CPoint: public tagPoint
{
public:
// Конструкторы
CPoint( );
CPoint(int initX, int initY);
CPoint(POINT initPt);
CPoint(SIZE initSize);
CPoint(DWORD dwPoint);
// Операции (перегруженные)
. . .
BOOL operator==(POINT point)const;
BOOL operator!=(POINT point)const;
void operator+=(POINT point);
void operator-=(POINT point);
// Операторы, возвращающие значения типа CPoint
CPoint operator-( )const;
CPoint operator+(POINT point)const;
. . .
BOOL CPoint :: operator+=(POINT point) const
{
return (x += point.x) && (y += point.y);
}
Использование перегруженных операторов тривиально:
CPoint ptObj1(1, 2);
CPoint ptObj2(3, 4);
CPoint ptObj3 = ptObj1 + ptObj2; // В ptObj3 установлены значения (4, 6)
В заключение хочется сказать, что при неаккуратном использовании механизм перегрузки операций может привести к получению очень сложного для восприятия кода. В то же время, при правильном написании, перегруженные операции могут резко улучшить читаемость программы и повысить вероятность повторного применения существующих алгоритмов.
3. Иерархия объектов в С++
Теперь перейдем к более детальному рассмотрению трёх ключевых идей объектно-ориентированного программирования – инкапсуляции, наследования и полиморфизма – применительно к языку С++, и теснейшим образом связанного с ними понятия класса.