void f1(){…}

/* Описание действий, производимых функцией, возвращаемого значения, параметров */

int f2(){…}

/* Описание действий, производимых функцией, возвращаемого значения, параметров */

char * f3(int a, int b){…}

/* Зачем нужна переменная */

int GlobalData;

main. cpp

#include ”funcs. h”

int main(int argc, char *argv[]){

f1();

int a = f2(…);

char *b = f3(…);

return 0;

}

Файловый ввод-вывод

lОткрытие файла

lЧтение данных из файла

nНеформатированное чтение

nЧтение символов

nЧтение строк

nФорматированный ввод

lЗапись данных в файл

nНеформатированная запись

nЗапись символа

nЗапись строки

nФорматированный вывод

lЗакрытие файла

lДополнительные функции

lФункции работы с файлами описаны в заголовочном файле stdio. h

Открытие файла

lФункция открытия потока имеет формат:

FILE* fopen(const char* filename, const char* mode);

lПри успешном открытии потока функция возвращает указатель на структуру типа FILE

lПараметры

nимя файла в виде,

nрежим открытия файла

l"г" — файл открывается для чтения;

l"w" — открывается пустой файл для записи (если файл существует, он стирается);

l"а"' — файл открывается для добавления информации в его конец;

lможет быть добавлена буква t (текстовый режим) или b (двоичный режим), отличающиеся обработкой символов перехода на новую строку. По умолчанию файл открывается в текстовом режиме.

Закрытие файла

lФайл закрывается либо при завершении программы, либо явным образом с помощью функции:

НЕ нашли? Не то? Что вы ищете?

int fclose(FILE *pfile);

lПеред закрытием файла информация из связанных с ним буферов выгружается на диск.

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

Чтение и запись по одному символу

int fputc(int c, FILE *stream);

int fgetc(FILE *stream);

с – выводимый символ

stream – указатель на файл, полученный функцией fopen().

Чтение и запись строками

char *fgets(char *s, int n, FILE *stream);

int fputs(const char *s, FILE *stream);

s – указатель на строку

n – максимальное количество символов в строке.

stream – указатель на файл, полученный функцией fopen().

Форматированный ввод-вывод

int fprintf(FILE *stream, const char *format[, argument, ...]);

int fscanf(FILE *stream, const char *format[, address, ...]);

lstream – указатель на файл, полученный функцией fopen().

lОстальные параметры как у функций printf и scanf.

lИмеются аналоги и для других функций, ориентированные на стандартные потоки ввоода-вывода

Неформатированный ввод-вывод

size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream);

ptr – указатель на область памяти

size – размер элемента в байтах

n – количество элементов

stream – указатель на файл, полученный функцией fopen().

Дополнительные функции

int feof(FILE *stream);

lВозвращает не равное нулю значение, если достигнут конец файла, в противном случае 0;

int ferror(FILE* stream);

lВозвращает не равное нулю значение, если обнаружена ошибка ввода/вывода, в противном случае 0.

Пример вывода в файл

FILE *out = fopen("test. txt","wt");

fputs("Hello, World!", out);

fclose(out);

Пример чтения из файла

FILE *in = fopen("test. txt","rb");

while(!feof(in)){

char c = fgetc(in);

printf(“Прочитан символ с кодом %i \n", (int)c);

}

fclose(in);

Математические функции

lОписаны в файле math. h

lОни позволяют получить

nабсолютное значение (abs, fabs),

nокругленное число (ceil, floor),

nквадратный корень (sqrt),

nстепень (pow),

nзначения тригонометрических функций (sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, atan2),

nэкспоненту (ехр),

nлогарифм (log, log10),

nдробную и целую части числа (modf),

nостаток от деления (fmod)

nи другие (см. help)

Функции работы со строками

lЗаголовочный файл string. h

lБиблиотека С содержит функции

nкопирования строк (strcpy, strncpy),

nсравнения (strcmp, strncmp),

nобъединения строк (strcat, strncat),

nпоиска подстроки (strstr),

nпоиска вхождения символа (strchr, strrchr. strpbrk),

nопределения длины строки (strlen)

nи другие (см help).

Динамические структуры данных

lВо время выполнения программы количество данных может существенно изменяться.

lЗадачи:

nвставка и удаление элементов

nдоступ к произвольному элементу

Работа с массивами. Доступ к произвольному элементу.

lЭлементы массива располагаются в памяти последовательно.

lЗная размер элемента и адрес начала массива можем получить адрес элемента одним выражением.

Добавление элемента в массив

lПоследовательность действий

nВыделить память для нового массива

nПереписать старый массив

nДобавить новый элемент

nОсвободить память от старого массива

lАналогичные действия потребуется выполнить если количество элементов уменьшилось.

lПри вставке элемента в середину массива алгоритм усложняется.

lБольшие временные затраты. При больших размерах массива держать в памяти сразу два массива может быть затруднительно.

Пример добавления элемента в массив.

int n = 10; // Количество элементов в массиве

int *m = new int[n]; // Сам массив

// Работа с массивом из 10 элементов

// потребовалось хранить 11 элементов

int m1 = new int[n+1];

for(int i=0;i<n;i++) m1[i] = m[i];

m1[n] = новый элемент;

delete[] m;

m = m1;

n++;

// Работаем дальше с массивом из 11 элементов

Структуры.

lПонятие структуры в С++ эквивалентно понятию записи.

lПеред использованием структура должна быть объявлена.

lЭлементами структуры могут быть данные любых типов.

lОбъявление структуры

struct имя_структуры {

тип_элемента название;

тип_элемента название;

тип_элемента название;

};

Примеры объявления структур

struct S1{

int a;

int b;

};

struct S2{

char *s;

int mas[100];

};

struct S3{

S1 simple_struct;

S2 big_struct;

S3 *next_element_in_list;

};

Обращение к элементам структур

lЕсли переменная является структурой, то для обращения к элементу структуры используется операция “.”

lНапример:

S1 ss;

ss. a = 5;

ss. b = 10;

int tmp = ss. a + ss. b;

Обращение к элементам структур через указатели

lЕсли переменная является указателем на структуру, то доступ к элементам структуры можно получить разыменовав указатель:

S1 * ss = new S1;

(*ss).a = 5;

(*ss).b = 10;

int tmp = (*ss).a + (*ss).b;

delete ss;

lУпростить доступ можно с помощью специальной операции “->”.

S1 * ss = new S1;

ss->a = 5;

ss->b = 10;

int tmp = ss->a + ss->b;

delete ss;

Понятие списка

lСписок – динамическая структура данных, в которой соседние элементы связаны между собой.

lКак правило, в С++ элементами списка являются структуры одного типа, а связь элементов осуществляется с помощью указателей.

lВ зависимости от вида связи между элементами списка могут быть созданы различные виды списков.

Односвязный линейный список

Двусвязный линейный список

Доступ к элементам односвязного списка

lНеобходимо хранить адрес первого элемента списка.

lПеремещаясь от первого элемента ко второму и т. д. можно получить доступ к любому элементу.

lПризнак достижения последнего элемента – отсутствие указателя на следующий элемент.

Вставка элемента в список

Удаление элемента из списка

Реализация списка с помощью структур С++

struct LIST_ELEMENT{

int a;

char s[10];

LIST_ELEMENT *next_element_of_list;

};

LIST_ELEMENT * first_element_of_list = NULL;

Поиск нужного элемента

lПредположим, требуется найти элемент с номером K. Будем считать, что элементы списка нумеруются с нуля.

LIST_ELEMENT *p = first_element_of_list;

for(int i = 0; i<K; i++){

if(p == NULL){

// Ошибка! Элемент не найден.

}

p = p->next_element_of_list;

}

// в этой точке программы p указывает на //элемент списка с номером K

Добавление элемента в начало списка

LIST_ELEMENT *new_elt =

new LIST_ELEMENT;

new_elt->a = 10;

strcpy(new_elt->s,”Новый”);

new_elt->next_element_of_list = first_element_of_list;

Добавление элемента в конец списка

LIST_ELEMENT *new_elt = new LIST_ELEMENT;

new_elt->a = 10;

strcpy(new_elt->s,”Новый”);

new_elt->next_element_of_list = NULL;

LIST_ELEMENT *p = first_element_of_list;

if(p == NULL)

first_element_of_list = new_elt;

else{

while(p->next_element_of_list != NULL)

p = p->next_element_of_list;

p->next_element_of_list = new_elt;

}

Удаление первого элемента списка

if(first_element_of_list != NULL){

LIST_ELEMENT *tmp = first_element_of_list;

first_element_of_list =

first_element_of_list->next_element_of_list;

delete tmp;

}

Виды структур данных

lОбычно внутренняя структура списка скрывается от конечного пользователя.

lПользователю предоставляется некоторый набор функций для работы со списком.

lВ зависимости от функций, входящих в этот набор, могут быть реализованы различные динамические структуры данных:

nСписок

nОчередь

nСтек

nи т. д.

Абстрактные типы данных

Основные вопросы

lОпределение типа и поколения языков программирования

lПонятия абстракции и абстрактного типа данных

lПонятие инкапсуляции и ее преимущества

lСпецификация и реализация абстрактного типа данных

lРеализация абстрактных типов в С++

Определение типа

lТип данных - множество с операциями (алгебра), учитывающая следующее:

nесли дан новый тип, то можно описывать и инициализировать переменные этого типа;

nесли дана переменная некоторого типа, то можно определить и изменить ее текущее значение;

nесли даны два значения определенного типа, то можно сравнить их, по крайней мере на равенство или неравенство;

nописание типа дает некоторую интерпретацию определяемым синтаксисом языка символам, которые вводятся для обозначения констант.

lЕсли два типа отличаются по любым из перечисленных факторов, то такие типы считаются разными

Поколения языков

lПервое поколение - языки с минимальными возможностями типизации

nПредоставляют лишь средства для описания переменных простых типов и массивов; никаких новых типов вводить нельзя.

nФортран, Алгол-60.

lВторое поколениеязыки, предоставляющие программисту основные конструкторы типов: массивы, записи, объединения

nПЛ/1, Алгол-68, Паскаль, С

nТип рассматривается как множество значений, получаемых из базисных множеств с помощью конструкторов.

nВсе операции над типами данных предопределенные — определяемые языком, а не программистом.

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

lТретье поколение – языки, предоставляющие программисту средства определения абстрактных типов данных

nС++, C#, Java

nТипы понимаются как множества с операциями.

Понятие абстракции

lАбстракция — это суждение или представление о некотором объекте, которое содержит только свойства, являющиеся существенными в данном контексте.

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

lВнутри группы нужно изучать лишь те свойства, которые отличают ее отдельные элементы друг от друга. Это значительно упрощает элементы группы.

lПри необходимости детального изучения объектов нужно рассматривать их менее абстрактные представления.

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

Понятие абстрактного типа данных

lПри пошаговом уточнении программы задача разбивается на подзадачи до тех пор, пока их решение не будет выражено в нескольких строках языка программирования, при этом производится одновременная декомпозиция и программных единиц, и структур данных.

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

lДекомпозиция задачи производится на точно определенные подзадачи и типы данных.

Определение абстрактного типа данных

lАбстрактный тип данных — это способ определения некоторого понятия в виде класса объектов с некоторыми свойствами и операциями.

lТак как свойства обычно выражаются в терминах операций, то абстрактный тип данных часто отождествляют с соответствующим множеством операций.

lНапример:

nопределение понятия стека в терминах операций " втолкнуть элемент в стек", " создать новый стек", "выдать верхний элемент" и т. д.

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

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

Понятие инкапсуляции

lИнкапсуляция — это способ объединения в единое целое подпрограмм и данных, которые они обрабатывают.

lИнкапсуляция предшествует абстрактным типам данных и поддерживает их.

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

Преимущества инкапсуляции

lВ капсулу группируются логически связанные операции и объекты, непосредственно реализуя некоторое абстрактное понятие.

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

lВнешнюю по отношению к капсулам часть программы можно рассматривать как абстрактную — работающую посредством абстрактных операций с абстрактными объектами.

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

lСпецификация типа данных в точности определяет, какие возможности предоставляет капсула внешней среде.

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

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

nЗащита страхует также от возможных ошибок и попыток незаконно использовать внутренние представления данных.

lУпрощение процесса отладки.

nОграниченный доступ к капсуле исключает влияние ошибок во внешней среде на его функционирование. Поэтому программу можно строить последовательно, по очереди подключая каждую капсулу. После присоединения капсулы можно считать, что все появившиеся новые ошибки содержатся в этой капсуле, а не в уже отлаженных. Другими словами, границы капсулы препятствуют распространению ошибок по всей программе.

lУпрощение модификации программы.

nБолее вероятно, что те места программы, в которых потребуется произвести изменения, окажутся расположенными близко друг от друга.

Спецификация и реализация

lОбъявление абстрактного типа данных состоит из двух частей

nСпецификация – предназначается для сопряжения абстрактного типа данных с внешней средой. Содержит описание операций, которые могут быть выполнены над типом.

nРеализация – раскрывает семантику операций, инкапсулируя ее от внешней среды.

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

lТочность

lПонятность

lКонструктивность

lМощность

lМинимальность

lМодифицируемость

Реализация абстрактного типа данных

lВключает в себя конкретное описание объектов определяемого типа и реализацию операций этого типа.

lОбъекты описываются в терминах простых или составных типов или ранее определенных абстрактных типов.

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

Абстрактные типы данных в С++

lАбстрактные типы данных в С++ создаются с помощи синтаксической конструкции называемой «классом».

lПредназначение понятия класса состоит в том, чтобы предоставить программисту инструмент для создания новых типов, столь же удобных в обращении сколь и встроенные типы.

lВ идеале тип, определяемый пользователем, способом использования не должен отличаться от встроенных типов, только способом создания.

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

lХорошо выбранные типы, определяемые пользователем, делают программу более четкой и короткой.

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

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

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

Состав класса

lВнутренние переменные класса называются атрибутами.

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

lОбъекты класса создаются и инициализируются методами, специально для этой цели описанными. Эти методы называются конструкторами.

lМетод может быть специальным образом описан для "очистки" каждого классового объекта при его уничтожении. Такой метод называется деструктором.

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

struct date {

int month;

int day;

int year;

};

date today;

void set_date(date*, int, int, int);

void next_date(date*);

void print_date(date*);

// ...

Спецификация класса в С++

class имя_класса{

модификатор_доступа:

[атрибуты]

[методы]

модификатор_доступа:

[атрибуты]

[методы]

};

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

class date{

private:

int month, day, year;

public:

void set(int, int, int);

void get(int*, int*, int*);

void next();

void print();

};

Модификаторы доступа

lprivate

lpublic

lprotected

Работа с классом

date d1;

data *d2 = new date;

d1.set(5,5,2005);

d2->set(6,6,2006);

d1.next();

d2->print();

d1.year = 2007; // ошибка

delete d2;

Реализация класса

void date::print()

{

printf(“%i.%i.%i”,day, month, year);

}

Разделение спецификации и реализации

date.h

class date{

private:

int month, day, year;

public:

void set(int, int, int);

void get(int*, int*, int*);

void next();

void print();

};

date. cpp

#include “date. h”

void date::print()

{

printf(“%i.%i.%i”,day,

month, year);

}

Конструкторы

lИспользование для обеспечения инициализации объекта класса функций вроде set_date() (установить дату) неэлегантно и чревато ошибками.

lПоскольку нигде не утверждается, что объект должен быть инициализирован, то программист может забыть это сделать, или (что приводит, как правило, к столь же разрушительным последствиям) сделать это дважды.

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

lПоскольку такая функция конструирует значения данного типа, она называется конструктором.

lКонструктор распознается по тому, что имеет то же имя, что и сам класс.

lУ конструктора отсутствует возвращаемое значение.

Создание и использование конструктора

class date{

private:

int month, day, year;

public:

date(int day, int month, int year);

date(char *str); // дата в строковом представлении

date(int day); // день, месяц и год сегодняшние

};

date d1; // ошибка

date d2(5,5,2005);

date *d3 = new date(6,6,2006);

Деструктор

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

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

lИмя деструктора для класса X есть ~X() ("дополнение конструктора").

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

lДеструктор вызывается когда переменная выходит из области видимости (для класса памяти auto) или при использовании оператора delete.

Пример деструктора

class char_stack{

private:

int size;

char* top;

char* s;

public:

char_stack(int sz)

{ top=s=newchar[size=sz]; }

~char_stack(){ delete[] s; } // деструктор void push(char c) { *top++ = c; }

char pop() { return *--top;}

};

Inline

lПри программировании с использованием классов очень часто используется много маленьких функций.

lЭто может страшно понизить эффективность, потому что стоимость вызова функции (хотя и вовсе не высокая по сравнению с другими языками) все равно намного выше, чем пара ссылок по памяти, необходимая для тела функции.

lЧтобы справиться с этой проблемой, был разработан аппарат inline - функций. Метод, определенный (а не просто описанная) в описании класса, считается inline.

lЭто значит, что при компиляции программы вызов функции будет заменен на подстановку ее тела в место вызова.

Ссылки на себя

lВ методе на атрибуты, для которого он был вызван, можно ссылаться непосредственно.

lУказатель на объект, для которого вызван метод, является скрытым параметром метода. На этот неявный параметр можно ссылаться явно как на this.

lПри ссылке на атрибуты использование this излишне.

lГлавным образом this используется при написании методов, которые манипулируют непосредственно указателями.

Перегрузка операторов и функций

Перегрузка функций

lБывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя.

lЕсли это имя мнемонично, то есть несет нужную информацию, это делает программу более понятной, поскольку для каждого действия требуется помнить только одно имя.

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

lКомпилятор определяет, какую именно функцию требуется вызвать, по типу передаваемых параметров.

lЭтот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует.

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

Пример перегрузки функций

Имеется четыре варианта функции, определяющей наибольшее значение:

// Возвращает наибольшее из двух целых:

int max(int, int);

// Возвращает подстроку наибольшей длины:

char* max(char*. char*);

// Возвращает наибольшее из первого параметра и длины второго:

int max (int, char*);

// Возвращает наибольшее из второго параметра и длины первого:

int max (char*, int);

Вызов различных функций

lПри вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции (в приведенном примере будут последовательно вызваны все четыре варианта функции).

void f(int a, int b, char* C; char* d){

cout « max (a, b) « max(c, d) « max(a. c) « max(c, b);

}

Неоднозначности при вызове перегруженных функций

lНеоднозначность может появиться при:

nпреобразовании типа;

nиспользовании параметров-ссылок;

nиспользовании аргументов по умолчанию.

Пример неоднозначности при преобразовании типа

#include <iostream.h>

float f(float i){

cout « "function float f(float i)" « endl;

return i;

}

double f(double i){

cout « "function double f(double i)" « endl;

return i*2;

}

int main(){

float x = 10.09;

double у = 10.09;'

cout « f(x) « endl; // Вызывается f(float)

cout « f'(y) « endl; // Вызывается f(double)

/* cout « f(10) « endl; Неоднозначность - как преобразовать 10: во float или double? */

return 0;

}

Пример неоднозначности при использовании параметров-ссылок

lФункции

int f(int a, int b);

int f(int a, int &b);

lВызов

int a = 5, b=10;

f(a, b);

lКомпилятор не сможет узнать, какая из этих функций вызывается, так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке.

Пример неоднозначности при использовании аргументов по умолчанию

#include <iostream. h>

int f(int a){return a;}

int f(int a, int b=1){return a * b;}

int main(){

cout « f(10, 2); // Вызывается f(int. int)

/* cout « f(10); Неоднозначность - что вызывается: f(int, int) или f(int) */

return 0;

}

Правила описания перегруженных функций

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

lПерегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать.

lВ различных вариантах перегруженных функций может быть различное количество параметров по умолчанию.

lФункции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и const int или int и int&).

Перегрузка операторов

lТретье поколение ЯП – языки, предоставляющие программисту средства определения абстрактных типов данных

nС++, C#, Java

nТипы понимаются как множества с операциями.

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

lВ этом случае операции, обозначаемые одним и тем же символом будут обозначать приводить к выполнению совершенно различных действий в зависимости от контекста.

Перегруженный оператор “+”

int a,b;

double c, d;

char *e,*f;

a = a + b;

с = с + d;

e = f + a;

string s1= “abc”, s2 = “def”, s3;

s3 = s1 + s2; // s3 = “abcdef”;

Правила перегрузки операций

lПерегрузка операций осуществляется с помощью функций специального вида (функций-операций)

nФункция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции:

nтип operator операция ( список параметров) { тело функции }

lпри перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных тинах данных;

lдля стандартных типов данных переопределять операции нельзя;

lфункции-операции не могут иметь аргументов по умолчанию;

lфункции-операции наследуются (за исключением =);

lфункции-операции не могут определяться как static.

lФункцию-операцию можно определить тремя способами:

nкак метод класса,

nдружественная функция класса,

nлибо обычной функцией.

Переопределение операции с помощью метода класса

class Square{

private:

int x, y;

int size;

public:

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

Square &operator++() {size++; return *this;}

};

Square s;

(++s).print();

Переопределение операции с помощью обычной функции

class Square{

private:

int x, y;

public:

int size;

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

};

Square &operator++(Square &s) {s. size++; return s;}

Square s;

(++s).print();

Переопределение операции с помощью функции - друга

class Square{

private:

int x, y;

int size;

public:

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

friend Square &operator++(Square &s);

};

Square &operator++(Square &s) {s. size++; return s;}

Square s;

(++s).print();

Унарная постфиксная операция

lОперации постфиксного инкремента и декремента должны иметь первый параметр типа int.

lОн используется только для того, чтобы отличить их от префиксной формы:

class Square{

private:

int x, y;

int size;

public:

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

Square &operator++(int) {size++; return *this;}

};

Square s;

s++;

s. print();

Перегрузка бинарных операций

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

lЕсли функция определяется вне класса, она должна иметь два параметра типа класса.

class Square{

private:

int x, y;

int size;

public:

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

bool operator>(const Square &s)

{

if(size >s. size)

return true;

else

return false;

}

};

Square s1,s2;

if(s1 > s2) printf(“s1 больше!”);

class Square{

private:

int x, y;

int size;

public:

Square(){x = 0; y = 0; size = 10;}

print(){printf(“ x=%i, y= %i, size=%i\n”,x, y,size);}

friend bool operator>(const Square &s1, const Square &s2);

};

bool operator>(const Square &s1, const Square &s2)

{

if(s1.size >s2.size)

return true;

else

return false;

}

Square s1,s2;

if(s1 > s2) printf(“s1 больше!”);

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

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7