240-1677 (Объективное программирование), страница 4

2016-07-31СтудИзба

Описание файла

Документ из архива "Объективное программирование", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "240-1677"

Текст 4 страницы из документа "240-1677"

}

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

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

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 дней\n",(long)b-(long)a);

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

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

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

c = b;

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

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

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

}

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

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

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

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

то в классе "zzz" необходимо определить конструктор

class zzz

{

int par_zzz;

----------------- входной тип (класс)

zzz(xxx p); или

zzz(xxx& p);

L-------------------- выходной тип (класс)

};

void zzz::zzz(xxx &p)

{

par_zzz = ... p.par_xxx ...;

элемент объекта----- L-------элемент объекта

выходного класса входного класса

}

class xxx

{

friend class zzz;

int par_xxx;

};

со следующими свойствами:

- объект класса "zzz", который является выходным при преобразовании типов доступен как в любом конструкторе через ссылку на текущий объект this;

- элементам выходного объекта (например, par_zzz) должны быть присвоены значения с явным или неявным использованием ссылки this

this->par_zzz = ...

(*this).par_zzz = ...

par_zzz = ...

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

Значение переменной или элементов входного объекта могут использоваться как аргументы при преобразовании типов;

- для доступа из функции класса "zzz" к приватной части объекта класса "xxx" класс "zzz" должен быть объявлен дружественным

в определении класса "xxx".

В качестве примера рассмотрим обратное преобразование базового типа long к типу dat - количество дней от начала летоисчисления преобразуется к дате. Здесь же рассмотрим другой класс

- man, в котором одним из элементов приватной части является дата.

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

class dat

{

int day,month,year;

public:

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

dat(man&); // Преобразование man в dat

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

dat(int,int,int); //

dat(char *); //

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

};

class man

{

friend class dat; // Класс dat дружественен

... // классу man

int d,m,y; // Элемент "дата" в объекте

... // класса man

public:

man(dat&); // Преобразование dat в man

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

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

};

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

//формальный параметр - неявная ссылка - для входного класса

//-------------------------------------------------------void dat::dat(man& p)

{

day = p.d;

month = p.m;

year = p.y;

}

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

//формальный параметр типа long передается по значению

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

void dat::dat(long p)

{

year = p / 365.25; // Число лет с учетом високосных

p = p - (year-1)*365L - year/4; // Остаток дней в текущем году

year++; // Начальный год - 0001

for (month=1; p > 0; month++) // Вычитание дней по месяцам

{

p -= days[month];

if (month == 2 && year % 4 == 0) p--;

}

month--; // Восстановление последнего

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

if (month == 2 && year % 4 == 0) p++;

day = p + 1;

}

void main()

{

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

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

int c;

long d;

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

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

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

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

c = b; // Неявное преобразование при присваивании

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

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

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

}

5.3 Переопределение операций new и delete

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

Операции создания и уничтожения объектов в динамической памяти могут быть переопределены следующим образом:

void *operator new(size_t size);

void operator delete (void *);

где void * - ссылка на область памяти, выделяемую под объект,

size - размер объекта в байтах.

Переопределение этих операций позволяет написать собственное

распределение памяти для объектов класса.

5.4 Переопределение операций [], (), ->

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

Переопределение () :

-------------------class one

{

public:

typeout operator()(type1,type2);

};

Вызов:

type1 a; // Вызов оператора совпадает с

type2 b; // синтаксисом вызова функции

one obj; // с именем данного объекта

... obj(a,b) ... эквивалентно obj.operator()(a,b)

Переопределение -> :

------------------class two

{

public: type Y;

};

class one

{

two operator->(); // Операция должна возвращать объект

или two* operator->(); // или ссылку на объект класса two,

}; // в котором определен элемент Y

Вызов:

one obj;

... obj->Y .. эквивалентно (obj.operator->()) ->Y

Переопределение [] : используется для моделирования виртуальных

-------------------- массивов элементов определенного типа.

class text_page

{

char **page; // Массив ссылок на строки

public:

int operator[](char*); // Ассоциативный поиск индекса

// по строке

char* operator[](int); // Выделение строки по индексу

};

5.5 Переопределение операции копирования объектов

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

Kaк известно, определение объекта класса в виде

=

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

Eсли функция возвращает объект, то оператор return также выполняет копирование объекта - операнда в объект назначения.

Taкое копирование не корректно в том случае, если объекты содержат ссылки на другие объекты или переменные в динамической памяти. В этом случае можно воспъльзоваться специальным конструктором копирования, параметром котрого является неявная ссылка на объект - источник, а this указывает на объект приемник. Будучи определенным, он вызывается во всех вышеперечисленных случаях копирования объектов один в другой.

Пример корректного конструктора копирования для класса строк имеет вид:

class string

{

char *s; // Ссылка на строку

int sz; // Длина строки

public: string(string&);// Конструктор копирования

}; // создает копию строки в динамической

// памяти для объекта - приемника

string::string(string& right)

{

s = new char[right->sz];

strcpy(s,right->s);

}

Лекция 6. Производные классы

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

6.1 Вложенные классы

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

Понятие производного класса вводит в систему классов принцип

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

собственные цели и особенности.

Первый случай представляет собой обычный способ построения

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

Он имеет собственное имя (именован), по которому к нему можно обращаться как к объекту. В элементах-функциях нового класса можно

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

класса. Рассмотрим в качестве примера класс man - информация о

человеке, включающая в себя даты рождения и поступления на работу.

class man

{

char name[20]; // Другие элементы класса

char *address;

dat dat1; // Дата рождения

dat dat2; // Дата поступления на работу

public:

void newadr(); // Элемент-функция

man(char*); // Конструктор

}

//----- Функция "Изменить адрес проживания" ----------------void man::newadr()

{

int n;

char s[80]; // Строка нового адреса

if (address != NULL)

delete address; // Освободить память

printf("Введите новый адрес:");

gets(s);

address = new char[strlen(s)+1];// Занять новую память

strcpy(address,s); // Заполнить поле адреса

}

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

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

В случае, если конструктор объекта нового класса задан обычным образом, то перед вызовом этого конструктора будут вызваны конструкторы без параметров для входящих в него объектов старого класса. И наоборот, после вызова деструктора для объекта нового класса будут вызваны деструкторы вложенных объектов старого класса.

Однако при конструировании вложенных объектов им желательно передавать параметры. Поэтому при описании конструктора объекта нового класса можно в заголовке в явном виде указать тот вид конструктора объекта старого класса, который требуется. Кроме того, его параметры могут зависеть от параметров вызова конструктора нового класса:

class man

{

char name[20]; // Другие элементы класса

dat dat1; // Дата рождения

dat dat2; // Дата поступления на работу

public:

man(char *,char *,char *); // Конструкторы

man(char *);

}

//----- Конструктор класса man с неявным вызовом конструкторов

// для dat1 и dat2 без параметров

//----------------------------------------------------- man::man(char *p)

{

}

//----- Конструктор класса man с явным вызовом конструкторов

// для dat1 и dat2 с параметрами

//--------------------------------------------------------- man::man(char *p,char *p1, char *p2) : dat1(p1), dat2(p2)

{ ¦ ¦ ¦

// --- Тело конструктора --- ¦ ¦ ¦

} ¦ ¦ ¦

Вызов конструктора для ------------------ ¦ ¦

вложенного объекта dat1 ¦ ¦

В качестве параметра передается ------------- ¦

строка - второй параметр вызова ¦

конструктора для класса man Вызов конструктора для

вложенного объекта dat2

void main ------ Строка конструктора man

{ ¦

man JOHN("John","8-9-1958","15-1-1987");

} ¦ L------ Строка передается

Строка передается конструктору объекта

конструктору объекта dat2 в объекте man

dat1 в объекте man

6.2 Производные классы

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

Другой случай вложенности классов основывается на понимании

класса как совокупности данных и операций над ними. При этом

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

свойств старого "базового" класса, или "наследует" их: структура

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

Старый класс при этом называется базовым классом (БК), новый

- производным классом (ПК).

Синтаксис определения производного класса имеет вид:

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5167
Авторов
на СтудИзбе
438
Средний доход
с одного платного файла
Обучение Подробнее