Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Язык программирования С++ и его «подводные камни».DOC
Скачиваний:
39
Добавлен:
01.05.2014
Размер:
1.02 Mб
Скачать

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. Иерархия объектов в С++

Теперь перейдем к более детальному рассмотрению трёх ключевых идей объектно-ориенти­рованного программирования – инкапсуляции, наследования и полимор­физма – при­ме­ни­тель­но к языку С++, и теснейшим образом связанного с ними понятия класса.