Void display()
Double X
public:
pair (int n, double x)
{ N=n; X= x; }
friend pair & operator ++ (pair &); // префиксная
friend pair & operator++ (pair&, int); // постфиксная
pair& operator- - () // префиксная
{ N= N-1; X - = 1.0;
return * this; }
pair& operator- - (int k) // постфиксная
{ N= N-1+k; X - = 1.0 +k;
return * this;}
{ cout<< “\n N = “ <<N <<” X= “ << X; }
};
pair & operator ++ (pair & P) // префиксная
{ P. N + = 1; P.X +=1.0;
return P; }
pair & operator ++ (pair & P, int k) // постфиксная
{ P. N + = 1+k; P.X +=1.0 +k;
return P; }
{ pair A (9, 19.0); A.display();
++A; A.display ();
- - A; A.display ();
A++; A.display ();
A- -; A.display (); }
Результат:
N = 9 X = 19
N = 10 X = 20
N = 9 X = 19
N = 10 X = 20
N = 9 X = 19
Перегрузка операции присваивания
Перегрузку операции ”=” разрешается реализовывать только через компонентную функцию класса!
Рассмотрим перегрузку для класса комплексных чисел:
Complex& Complex:: operator = (Complex & z)
{ re = z.re; im = z. im;
return * this; }
При выполнении
c2=c1;
операция функция вызывается для объекта с2, объект с1 передается в нее как параметр. Данным объекта с2 присваиваются данные объекта с1.
Функция возвращает текущий объект для того, чтобы можно было использовать операцию множественного присваивания:
|
|
с3 = с2 = с1;
Заметим, что в данном случае перегружать операцию присваивания нет особой необходимости. Если не перегружать “=”, то компилятор генерирует операцию присваивания по умолчанию, которая выполняет побайтовое копирование данных.
Однако в некоторых случаях, при наличии в классе данных, являющихся указателями на динамические участки памяти, буквальное копирование может привести к утечкам памяти.
При прямом копировании объект2=объект1 указателю объекта2 присвоится значение указателя объекта1. Участок памяти с данными 2 станет недоступным программе происходит утечка памяти. Уничтожение объекта 1 автоматически уничтожит объект2.
В этом случае необходимо перегрузить операцию присваивания:
class A { int* data;
…
A (int a)
{ data = new int;
*data= a; }
A (A&z)
{ data = new int;
data=*(z.data);}
A& operator= (const A & z)
{delete data; или { *data = *(z.data);
data = new int; return *this;}
*data = *(z.data);
return * this}
…
};
Различие между копированием и присваиванием:
Дублирование объекта может быть выполнено и с помощью операции присваивания и с помощью вызова конструктора копирования.
class A { int x;
public:
A (int ax=0){ x=ax; }
int GetX () { return x; }
… };
void main ()
{ A m1(5), m2;
m2 = m1; // операция присваивания
A m3 = m1; // дублирование вызовом конструктора копирования
cout<< m1.GetX () << m2.GetX () << m3.GetX ();
}
В предложении:
m2 = m1;
передача значений выполняется в уже созданный объект. Дублирование объекта производится операцией присваивания по умолчанию.
В предложении:
A m3 = m1;
создается новый объект, и ему передаются данные копируемого объекта. В этом случае вызывается конструктор копирования по умолчанию.
|
|
В ряде случаев объекты должны создаваться в единственном экземпляре, создание идентичных объектов должно быть запрещено.
Блокировка копирования и присваивания:
Для того, чтобы исключить непреднамеренное дублирование объекта класса, надо в его закрытой части объявить конструктор копирования и перегрузку операции присваивания.
private:
A (A&);
A operator = (A &);
И тогда ни одно из приведенных в главной функции дублирований объектов скомпилировать не удастся.
Преобразование типов в классах пользователя
Иногда в выражениях целесообразно использовать переменные класса (объекты) с переменными других типов, для этого необходимо определить правила преобразования.
Это можно сделать с помощью перегруженного конструктора класса или с помощью операции – функции преобразования типов.
Рассмотрим опять класс Complex.
Недостаток описания состоит в том, что нельзя выполнять комплексные операции комплексного числа с арифметическими данными:
Complex c1(5.5, 6.5);
Complex c2= c1+ 2;
Complex c2= c1+ ‘0’;
В этом случае компилятор будет пытаться создать временный объект Complex (2), но наш конструктор с двумя параметрами, поэтому скомпилировать нам это не удастся.
Надо объявить конструктор такого вида:
Complex (float r =0, float i =0)
{ re = r; im = i; },
который позволит, например, создавать объекты:
Complex c1(1.5, 2.5);
Complex c1(4.5);
Complex c1;
Рассмотрим операторы описанные выше:
1) 2 преобразуется к 2.0,затем создается временный объект, вызовом конструктора с умалчиваемыми значениями, с данными (2.0, 0.0).
В результате создается объект с2 с данными (7.5, 6.5).
2) ‘0’ преобразуется в целое 48, которое преобразуется в вещественное
48.0, создается временный объект с данными (48.0, 0.0).
В результате создается объект с данными (53.5, 6.5).
Иногда бывает необходимость преобразовать переменные некоторого класса в базовые типы.
Проблема решается с помощью специального оператора преобразования типа.
Рассмотрим это преобразование для класса stroka. Преобразование типа stroka в тип char*.
Чтобы объекты класса можно было бы передавать функциям модуля string.h, надо в описание класса включить компонентную функцию:
1)
operator char* () { return ch; } (1)
И тогда в предложении
stroka s1(“string”)
char* s = s1;
произойдет преобразование объекта s1 к типу char*, правило (1),и переменной s присвоится значение “string”.
2)
Аналогично можно определить в классе преобразование:
operator int () { return len; } (2)
И тогда в предложении:
int l = s1;
переменная класса (объект s1) преобразуется к целому значению по правилу описанному в операторе преобразования (2).
Вопросы:
8) В чем суть объектно-ориентированного подхода к программированию? Дать определение класса.