Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

технология программирования

.pdf
Скачиваний:
84
Добавлен:
30.03.2015
Размер:
1.38 Mб
Скачать

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

#include <stdio.h> //Пример использования перечислений.

void main()

 

 

{

 

 

enum test {t1,t2,t3,t4,t5} t;

 

 

printf("t1=%d t4=%d\n", t1,t4);

//

0 3

t=t1; printf("t=t1 = %d\n ", t);

//

0

t++; printf("t++ = %d\n ", t);

//

1

t=t4; t-=2;

 

 

printf("t4=%d t-=2 = %d\n ", t4, t);

//

3 1

t--; printf("t-- = %d\n ", t);

//

0

t=t4; t=t-t2; printf("t= = %d\n ", t);

//

2

}

 

 

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

Пример.enum{ false, true} boolean; объявляет переменную boolean с допустимыми значениями false, true.

Основное назначение перечислений улучшить читаемость программы.

2.8.5 Переименование типов typedef

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

typedef имя ранее определенного типа имя нового типа1

, имя нового типа2 … ;

Новое имя становится синонимом имен ранее определенным.

Например:

typedef float real; Теперь вместе fboat можно использовать real. typedef char symbol;

Часто используется для переопределения структур. typedef struct st

{

char name [30]; char group [4]; int god;

} STUDENT;

Теперь для определения переменной можно записать st AN;

или STUDENT AN;

Область действия зависит от расположения оператора typedef. Если определение находится внутри функции, то область действия локальна и ограничена этой функцией. Если вне, то – глобальна.

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

21

Пример1.

 

typedef char arr [40];

// FIOмассив символов

arr FIO, *adres;

// adresуказатель на массив символов

Это эквивалентно char FIO[40], *adres;

Пример2.

 

typedef int* Pi;

//объявлен новый тип Pi-указатель на целое.

typedef void (*pfn) ( );

//объявлен новый тип pfn-указатель на функцию, не-

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

typedef void (*pfnI) (int); // объявление типа pfnI-указатель на функцию с одним аргументом типа int, невозвращающую значения

typedef void (*pptn[10]) (); //объявление типа pptnмассив из 10 указателей на функцию, не возвращающую значения, с любым списком аргументов.

Объявление typedef применяется

1)для создания удобных распознаваемых имен часто встречающихся;

2)для вложенных типов;

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

2.9 Файлы

Прежде чем читать информацию из файла или записывать в него, нужно его открыть. В библиотеке <stdio.h> для этого имеется специальная функция

FILE *fopen(char *fname, char *mode);

 

где *fname – имя файла, *mode – режим (табл.3).

Функция возвращает указа-

тель (ссылку) на файл, который должен быть предварительно описан.

Например, FILE *fu;

*fu указатель на файловый тип

Пример. Объявим указатели на переменные файлового типа

FILE *uin, *uout;

// (указатели на переменные файлового типа)

uin = fopen(“name1”, “r”); // открыть файл “name1” для чтения и далее идентифицировать как uin

uout = fopen(”name2”, “w”); // открывается для записи и связывается с идентификатором uout.

Если производиться открытие несуществующего файла, то он создается.

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

#include <stdio.h> // работа с файлами и константа NULL (FILEOPEN.C)

#include <stdlib.h> //для ф-ии exit()

void main()

{

FILE *fp;

if ((fp=fopen("test","w"))==NULL)

{

puts("Не могу открыть файл\n"); // печать строки exit(1);

}

22

puts("Файл открыт\n");

}

Таблица 3

 

Таблица режимов для открываемого файла

 

 

Режим

Действие

“r”

Открыть для чтения

“w”

Создать для записи

“a”

Открыть для добавления в существующий файл

“rb”

Открыть двоичный файл для чтения

“wb”

Создать двоичный файл для записи

“ab”

Открыть двоичный файл для добавления

“r+”

Открыть для чтения и записи

“w+”

Создать для чтения и записи

“a+”

Открыть для добавления или создать для чтения и записи

“r+b”

Открыть двоичный файл для чтения и записи

“w+b”

Создать двоичный файл для чтения и записи

“a+b”

Открыть двоичный файл для добавления или создать для чтения и

 

записи

“rt”

Открыть текстовый файл для чтения

“wt”

Создать текстовый файл для записи

“at”

Открыть текстовый файл для добавления

“r+t”

Открыть открыть текстовый файл для чтения и записи

“w+t”

Создать текстовый файл для чтения и записи

“a+t”

Открыть текстовый файл для добавления или создать для чтения и

 

записи

В библиотеке <stdio.h> определены также следующие функции работы с файлами ( fpуказатель на файл, возвращаемый функцией):

fopen( ) - открытие файла;

int fclose(FILE *fp) - закрытие файла. Возвращает нуль, если операция выполнена успешно и иное значение в противном случае. Функция является рекомендуемой, поскольку файлы при нормальном завершении закрываются автоматически; int puts(int ch, FILE *fp) -записать символа типа int в поток. Возвращается записанный символ, если операция была успешной. Если произошла ошибка возвращается EOF;

int gets(FILE *fp) - прочесть символ типа int из потока. Возвращает EOF, если достигнут конец файла, или произошла ошибка при чтении файла;

int feof(FILE *fp) - возвращает значение «истинно», если достигнут конец файла и нуль в противном случае;

Пример. while (!feof(fp)) {ch=getc(fp);}

Выполняется ввод символа, пока не достигнут конец файла;

23

int ferror(FILE *fp) - возвращает значение нуль, если обнаружена ошибка. Рекомендуется для обнаружения ошибок чтения и записи после каждой операции с файлами;

int fprintf(FILE *fp, const, char *string, …) - форматированная запись в файл.

Содержит указатель на файл и управляющую строку со списком;

int fscanf(ILE *fp, const,char *string) – форматированное чтение из файла;

unsign fread(void *buf, int bytes, int c, FILE *fp) - читает блок данных из потока

(буферный обмен); buf - указатель на область памяти, откуда проис-ходит обмен информации. с - количество единиц записи длиной bytes, которая будит считываться.

unsign fwrite(void *buf, int bytes, int c, FILE *fp) - пишется блок данных в поток;

int remove(char *filename)- уничтожается файл, возвращается нуль при успешной операции;

unsign rewind( FILE *fp)- устанавливается указатель на начало файла;

int fseck( FILE *fp, long numbyte, int orig) - установить указатель позиции файла в заданное место, numbyte - количество байт от точки отсчета (0,1,2), orig – макрос 0 - начало, 1- текущая позоция, 2 - конец;

void abort() - <stdlib.h> - немедленное прекращение программы без закрытия файлов и без освобождения буферов.

2.10Операторы динамического распределения памяти в С++

ВС++ введены 2 «интеллектуальных» оператора new и delete, освобождающие про-граммиста от необходимости явно использовать библиотечные функции malloс, calloс и free.

Оператор new выделяет блок памяти, необходимый для размещения переменной или массива (необходимо указывать тип и, в случае массива, размерность), и при этом можно присваивать вновь созданной переменной начальное значение.

New type_name [(инициатор)]; или New (type_name [(инициатор)]);

Возвращает указатель на объект а= new int[n] для неизвестного числа элементов. Оператор delete освобождает ранее выделенную память. Размер занятого блока для правильной работы delete, записывается в его начало и обычно занимает дополнительно 4 байта.

Примечание: Следует помнить, что реальный размер занятого блока не произволен, а кратен определенному числу байт (в С++3.0-16), поэтому, с точки зрения расхода памяти невыгодно резервировать много блоков под небольшие объекты.

Вслучае успешного выполнение new возвращает адрес начала занятого блока памяти, увеличенный на количество байт, занимаемых информацией о размере блока (т.е. возвращает адрес созданной переменной или адрес нулевого элемента созданного массива). Когда new не может выделить требуемую память, он возвращает значение (void *), в этом случае рекомендуется предусмотреть в программе реакцию. Например:

24

#include <stdio.h> //

#include <iostream.h> // для в\в в С++ int main()

{

int *u_i; double *u_d; char *string; int str_len=80;

u_i=new int; //Зарезервировать место под переменную типа int и //присвоить u_i ее адрес (Значение *u_i не определено)

u_d=new double(3.1415);// -"- и *u_d инициализируется знач. 3.1415 string=new char[str_len];

if (!(u_i && u_d && string))

{

cout<<"Не хватает памяти для всех динамически" "размещаемых переменных!";

return 1;

}

string[0]='Y';

string[1]='e';

string[2]='s';

string[3]='!'; string[4]='\0';

cout<<"\n u_i="<<u_i<<" случайное значение *u_i="<<*u_i ; delete u_i; //Освободить блок памяти, на кот указывает u_i cout<<"\n u_d="<<u_d<<" *u_d="<< *u_d ;

delete u_d; //Освободить блок памяти, на кот указывает u_D printf("\n string=%p ", string);

cout<<" string contents="<<string<<"\n"; delete string; // Можно и delete [str_len] string;

return 0; // но выдается предупр: размер массива игнорируется

}

Если указатель, на который действует оператор delete, не содержит адреса

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

Помимо проверки возвращаемого значения, в С++ для обработки ситуации нехватки памяти, программист может определить специальную функцию обработки, которая будет вызываться при неудачном выполнение оператора new и «пытаться» изыскать необходимую память, либо выдать сообщение и завершить выполнение программы библиотечной функцией exit или abort.

2.11 Директивы препроцессора

Условная компиляция

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

25

Для этого служат директивы #if, #elif, #else, #endif. Синтаксис условной конструкции:

#if <выражение1>

последовательность операторов //компилировать, если <выражение1> истинно. #elif <выражение2>

операторы//компилировать, если <выражение1> ложно, а <выражение2> истинно #elif <выражение3>

последовательность операторов // компилировать, если <выражения1 и 2>

ложны, а <выражение3> истинно

#else

операторы // компилировать, если все выражения ложны. #endif // конец для условной компиляции if.

Правило выполнения условных директив.

1.Для каждого #if должна быть соотвествующая #endif.

2.Директивы #elif и #else являются опциональными (исключают друг друга).

3.Число директив #elif между #if и #endif не ограничено. Директива #else должна быть одна и находиться перед #endif.

4.Аналогично оператору if ...else компилируется та секция, которая соответствует первому истинному выражению.

5.Если ни одно из выражений не истинно, то компилируется секция, следующая за частью #else.

6.Значение выражения должно быть целой константой; в выражении нельзя использовать операцию sizeof и преобразование типов.

Оператор defined или знак операции препроцессора

Он может применяться в директивах #if и #elif и позволяет проверить, был ли определен идентификатор или макрос.

void ShowMessage(char *msg)

{

#if defined(DOS_target) puts(msg);

#else MessageBox(NULL,msg,''MSG'',MB_OK);

#endif

}

Если макрос DOS_target определен, то функция ShowMessage использует для вывода сообщения функцию puts, в противном случае компилируется функция MessageBox для специального вывода.

Можно применять логическую операцию ! для проверки того, что идентификатор не определен. Функцию void ShowMessage(DOS_target) можно переписать таким образом

{# if !defined(DOS_target)

MessageBox(NULL,msg,''MSG'',MB_OK);

# else

puts(msg);

# endif

26

}

Директивы #ifdef и #ifndef

Эти директивы эквивалентны соответственно #if defined и #if !defined и позволяют проверить, определен идентификатор в данный момент или нет. Однако применение оператора defined является более предпочтительным, т.к. он позволяет проверить сразу несколько макросов в сложных логических выражениях.

# ifdef DOS_target

можно упростить до # if defined (DOS_target)

# ifndef NDEBUG

&&!defined(NDEBUG)

puts(msg);

puts(msg);

# endif

# endif

# endif

 

Данные директивы требуют наличия соответствующей директивы #endif, между ними может (но не обязательно) размещаться директива #else или #elif.

Директива #error

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

Ее структура: #error <сообщение об ошибке>

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

Директива #line

Позволяет изменить внутренний счетчик строк компилятора и имеет Структуру: #line номер строки["имя файла"]

Номер строки должен быть целой константой. В строке может присутство-вать опциональное имя файла. Эта директива изменяет предопределенный макрос _ _LINE_ _. Если присутствует имя файла, то меняется макрос

_ _FILE_ _. Директива используется, чтобы в процессе трансляции заменить наименование текущего файла исходного текста программы и (или) номер строки этого текста.

Предопределенные макросы

Компилятор автоматически определяет некоторые макросы ANSI.(ANSI – официальное название стандарта языка Си, препроцессора и библиотеки поддержки выполнения программы, принятое Американским Национальным институтом Стандартов)

__DATE__ - строка, представляющая дату, когда данный файл обрабатывался препроцессором (форма даты mm dd yyyy).

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

__STDC__значение является константой, равной 1, если установлено соответствие со стандартом ANSI, в противном случае макрос не определен. __TIME__строка, представляющая время в форме hh:mm:ss, когда данный файл обрабатывался препроцессором.

27

Директива #pragma

Позволяет управлять специфическими возможностями компилятора. Ее синтаксис: #pragma <директива>

Если реализация системы программирования, обнаружив pragma, ее не узнает, то система ее игнорирует. (Стандартных прагм не существует.)

Директивы #pragma, поддерживаемые компилятором C++:

аrgsusd - подавляет предупреждающее сообщение о том, что параметр xхх не использован для функции, следующей за директивой;

exit - позволяет указать функцию, которая должна быть вызвана перед завершением программы;

еxtref - заставляет компилятор включить ссылку на неиспользованную внешнюю переменную или функцию;

hdrfile специфицирует имя заранее откомпилированного файла-заголовка;

 

hdrignore - т.к. макросы и типы, определяемые в заголовочном файле,

могут

изменяться,

когда определяется другой макрос, компилятор не использует ин-

формацию из перекомпилированного заголовка,

если встречает директиву услов-

ной компиляции. Данная директива указывает,

что заголовок должен использо-

ваться, если

встречается в директиве условной компиляции;

 

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

inline - указывает, что компиляция текущего модуля должна производиться через ассемблер;

intrinsic - эта директива может быть использована, чтобы разрешить или запретить генерацию inline-кода для встроенной функции; (встроенная функция - это библиотечная процедура, для которой компилятор генерирует inline-код вместо вызова библиотеки);

obsolete - приводит к тому, что компилятор генерирует сообщение-предупреж- дение о том, что имя файла является устаревшей функцией. Ее можно использовать, чтобы сообщить другим программистам, что улучен код и предусмотрена для данной задачи новая функция;

option – позволяет включить в код опции командной строки компилятора; startup – является дополнительной к #pragma exit, позволяет указать функцию, которая должна выполняться до функции main;

warn – позволяет выборочно разрешать или подавлять предупреждающее сообщение. Если предупреждению предшествует знак «+», то выдача сообщения разрешается, если знак «-» , то запрещается.

Некоторые компиляторы, в частности ANSI, узнают директиву pragma, которая указывает на то, как плотно упакованы смежные члены в структуру, например: #pragma pack (n) , где n может быть 1,2 или 4, указывая, что имеет место выравнивание на границу байта, слова или двойного слова.

В языке С/С++ имеются также директивы подключения библиотек # include и макроподстановок #define [7, 8].

28

3 ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ

3.1 Объект

Наиболее важное понятие ООП – это объект. Объект - это логическая единица, которая содержит данные и правила (методы) обработки этих данных.

В С++ в качестве правил выступают функции, обрабатывающие эти данные. Внутри объекта данные и функции могут быть частными или приватными

(private), защищенным (protected) и общими (public) [4-10]. Объектно-

ориентированные языки характеризуютя абстракцией типов и обладают следующими важнейшими характеристиками:

1)инкапсуляцией,

2)наследованием,

3)полиморфизмом.

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

В терминологии С++ эти абстрактные типы данных называются классами. Концепция классов отражает принцип пакетирования: данные и функции, которые манипулируют этими данными, объединяются в единый пакет; устанавливаются специальные правила доступа к элементам пакета (функциям и данным). Максимально изолируются друг от друга интерфейс и реализация, отражающая всю совокупность действий, необходимых для поддержки интерфейса.

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

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

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

адля строк – это переопределение операций.

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

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

Определение класса напоминает определение структуры за исключением

29

следующего:

– класс обычно содержит одну или несколько спецификаций доступа: private, protected, public;

вместо ключевого слова struct может применяться ключевое слово class или union;

класс обычно включает в себя функции (функции - элементы или методы) наряду с элементами-данными.

обычно в классе имеются некоторые специальные функции, такие, как конструктор и деструктор. Конструктор имеет то же имя, что и класс. Деструктор имеет то же имя, но ему предшествует префикс-тильда ~.

Примеры объявления классов.

1) Определение класса Rect совпадает с определением структуры

struct Rect

 

//прямоугольник

{

 

 

int x1;

//

Элементы-данные Rect

int y1;

 

 

int x2;

 

 

int y2;

 

 

}

 

 

2)Класс Point содержит как элементы-данные, так и элементы-функции,

атакже использует спецификаторы доступа.

struct Point

 

// точка

{

 

 

private:

// Спецификатор частного доступа

int x;

// Элементы данных класса типа «Point»

int y;

// Члены-данные

public:

// Спецификатор общего доступа

int GetX();

// Элементы-функции класса

int GetY();

 

 

void SetX(int);

 

 

void SetY(int);

 

 

}

 

 

3) Класс Line

 

 

class Line

 

 

{

 

 

Point pt1;

//

точка 1 // Элементы-данные

Point pt2;

//

точка 2

int width;

// толщина

public:

// Спецификатор общего доступа

Line(int _x, int _y);

// Функция-элемент: конструктор!

~Line();

// Функция-элемент: деструктор!

}

 

30