Смекни!
smekni.com

Объективное программирование (стр. 5 из 10)

// поэтому может модифицироваться без изменения исходного объекта

//-------------------------------------------------------dat operator+(dat p,int n)

{

while (n-- !=0) p.next(p); // Вызов функции next для объекта p

return(p); // Возврат копии объекта x

}

//------ Операция "целое + дата" -----------------------------//1. Дружественная элемент-функция с полным списком аргументов

//2. Второй операнд класса dat - передается по значению,

//поэтому может модифицироваться без изменения исходного объекта

//-------------------------------------------------------dat operator+(int n, dat p)

{

while (n-- !=0) p.next(); // Вызов функции next для объекта p

return(p); // Возврат копии объекта p

}

//-------------------------------------------------------

void main()

{

int i;

dat a;

dat b(17,12,1990);

dat c(12,7);

dat d(3);

dat e;

dat *p = new dat[10];

clrscr();

e = a++;

d=b+15;

for (i=0; i<10; i++)

p[i] = p[i] + i;

delete[10] p;

}

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

В рассмотренном выше примере одним из параметров элементафункции - объект класса dat передается по значению, аналогично передается и результат операции. Эти два объекта определены неявно и создаются путем копирования из других объектов. Поэтому конструкторы для них не вызываются. Однако при уничтожении этих объектов производится вызов деструкторов. Таким образом, при проектировании класса и передаче его объектов по значению необходимо учитывать эти "лишние" объекты и деструкторы. Перечислим возможные случаи возникновения и уничтожения объектов в функции:

- объект -формальный параметр - создается в стеке, его значение представляет собой копию соответствующего фактического параметра, соответственно, конструктор для него не вызывается;

- автоматический объект x в теле функции создается в стеке перед вызовом функции. Если он инициализируется копией другого объекта (например, текущего), то конструктор для него не вызывается:

dat x = *this;

В противном случае вызывается конструктор с соответствующим набором параметров:

dat x;

dat x("13-JUL-1990");

- в любом случае перед выходом из функции вызывается деструктор для автоматического объекта;

- если функция имеет формальный параметр - объект класса, то

для него вызывается деструктор после выхода из функции (если это

переопределяемый оператор, то после вычисления выражения);

- если функция возврашает объект по значению (а не по явной

или неявной ссылке), то функция получает дополнительный неявный

параметр - ссылку на результат (см. "Неявные ссылки"), а по оператору return производится копирование возвращаемого объекта в

объект-результат по заданной ссылке. Однако при вызове такой

функции транслятор может по-разному формировать эту ссылку:

- если производится прямое присваивание результата функции,

то передается ссылка на объект в левой части операции присваивания;

- в остальных случаях на каждый вызов функции (по синтаксису

программы) вызывающей функцией формируется дополнительный автоматический объект. Конструктор для него не вызывается, так как он

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

Наиболее эффективным способом работы с объектом является передача его на вход (формальный параметр) и выдача на выход в качестве ссылки (преимущественно неявной). Однако в этом способе

все изменения, производимые при выполнении операции, происходят в

исходном объекте. Это не согласуется с интерпретацией большинства

бинарных операций, где результат является отдельным элементом

данных (объектов), а операнды не меняются. В качестве примера

рассмотрим вариант операции сложения "дата + целое", в котором

исходная дата увеличивается на заданное значение и одновременно является результатом операции.

class dat

{

int day,month,year;

public:

void next(); // Элемент-функция вычисления

// следующего для

dat& operator+(int); // Операция "дата + целое"

// с неявным операндом через this

//------ Операция "дата + целое" ------------------------------//1. Дружественная элемент-функция с полным списком аргументов

//2. Альтернативный вариант предыдущей функции

//3. Первый операнд класса dat - неявная ссылка на фактический

// параметр, значение меняется при выполнении операции

//4. Тело функции непосредственно в определении класса.

//-------------------------------------------------------friend dat& operator+(dat& p,int n)

{

while (n-- !=0) p.next();// Вызов функции next для объекта p

// по неявной ссылке на него.

return(p); // Возврат неявной ссылки неа p

}

// Операция "целое + дата" ------------------------------------//1. Дружественная элемент-функция с полным списком аргументов

//2. Второй операнд класса dat - неявная ссылка на фактический

// параметр, значение меняется при выполнении операции.

//3. Тело функции непосредственно в определении класса.

//-------------------------------------------------------

friend dat& operator+(int n, dat& p)

{

while (n-- !=0) p.next(); // Вызов функции next для объекта p

// по неявной ссылке на него.

return(p); // Возврат неявной ссылки на p

}

//-------------------------------------------------------

dat(); // Конструкторы

dat(int,int,int); // (см. предыдущие примеры)

dat(char *); //

~dat(); // Деструктор

}; // (см. предыдущие примеры)

//------ Операция "дата + целое" -------------------------------//1. Элемент-функция с неявным первым аргументом по ссылке this

//2. Меняется значение текущего объекта

//3. Результат - неявная ссылка на текущий объект

//--------------------------------------------------------dat& dat::operator+(int n)

{

while (n-- !=0)

next(); // Вызов функции next с текущим объектом this

return(*this); // Возврат неявной ссылки на объект (this)

}

//---------------------------------------------------------

void main()

{

int i;

dat a;

dat b(17,12,1990);

dat c(12,7);

dat d(3);

dat e;

dat *p = new dat[10];

e = a++;

d=b+15;

for (i=0; i<10; i++)

p[i] + i;

delete[10] p;

}

Лекция 5. Особенности переопределения различных операций

-------------------------------------------------------

Одной из важных возможностей использования переменных базовых типов в операциях является их преобразование к другим типам,

которое может производиться неявно (в бинарных арифметических

операциях и при присваивании), либо с использованием операции явного преобразования типа. Если преследовать целью достижение при

добавлении новых классов всей полноты свойств базовых типов данных, то аналогичные преобразования необходимо ввести и для этих

классов. Далее рассмотрим два возможных способа такого преобразования - стандартный, преобразование объекта класса к переменной

базового типа данных, и нестандартный - преобразование переменной

базового типа данных или объекта класса к объекту другого класса.

5.1 Преобразование к базовому типу данных

----------------------------------------

В качестве примера рассмотрим неявное преобразование объекта класса dat к базовым типам данных int и long. Сущность его заключается в вычислении полного количества дней в дате, заданной входным объектом (long) и количества дней в текущем году в этой же дате (int). Для задания этих операции необходимо переопределить в классе dat одноименные операции int и long. Переопределяемые операции задаются соответствующими элементами-функциями без параметров, ссылка на текущий объект входного класса передается через неявный параметр this. Тип результата совпадает с базовым типом, к которому осуществляется приведение и поэтому не указывается.

static int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

class dat

{

int day,month,year;

public:

operator int(); // Преобразование dat в int

operator long(); // Преобразование dat в long

long operator -(dat &p); // Операция dat-dat вычисляет

// разность дат в днях

dat(); // Конструкторы

dat(int,int,int); //

dat(char *); //

~dat(); // Деструктор

};

//------ Преобразование dat в int -----------------------------// Используется ссылка на текущий объект this

//------------------------------------------------------- dat::operator int()

{

int r; // Текущий результат

int i; // Счетчик месяцев

for (r=0, i=1; i<month; i++) // Число дней в прошедших

r += days[month]; // месяцах

if ((month>2) && (year%4==0)) r++; // Високосный год

r += day; // Дней в текущем месяце

return(r);

}

//------ Преобразование dat в long ---------------------------// Используется ссылка на текущий объект this

//------------------------------------------------------

dat::opertor long()

{

long r; // Текущий результат

r = 365 * (year-1) // Дней в предыдущих полных годах

r += year / 4; // Високосные года

r += (int)(*this); // Дней в текущем году - предыдущая

// операция (явное преобразование

return(r); // dat в int

}

//-------- Операция вычисления разницы двух дат ---------------// Первый операнд по ссылке на текущий объект this

// Второй операнд по неявной ссылке p

//-------------------------------------------------------long dat::operator-(dat& p)

{

return((long)(*this) - (long)p); // Преобразовать оба объекта

// к типу long и вычисл. разность

}

void main()

{

dat a("12-05-1990"); // Дата, заданная текстовой строкой

dat b; // Текущая дата

int c;

long d;

// Явное преобразование к long

printf("С 12-05-1990 прошло %4ld дней&bsol;n",(long)b-(long)a);

// Явное преобразование к int

printf("В этом году прошло %3d дней&bsol;n",(int)b);

// Неявное преобразование при присваивании

c = b;

d = b - a; // Операция dat-dat

printf("С 12-05-1990 прошло %4ld дней&bsol;n",d);

printf("В этом году прошло %3d дней&bsol;n",c);

}

5.2 Преобразование переменной к объекту класса

---------------------------------------------

Данный способ не является стандартным и требует проверки работоспособности в используемом компиляторе. Он основан на том

факте, что при компиляции явного или неявного преобразования объекта класса к базовому типу данных "xxx" вызывается переопределяемая операция "operator xxx()". Соответственно, при явном или неявном преобразовании к классу "zzz" должна вызываться переопределяемая операция "operator zzz". Логично, что такая операция должна быть определена в классе "zzz". Но тогда имя соответствующей элемента-функции будет "zzz::zzz", что соответствует конструктору. Таким образом, если необходимо определить явное или неявное преобразование от базового типа или класса "xxx" к классу "zzz",