Введение в объектно-ориентированное программирование на языке С++

Анализ прямых расширений языка С. Проведение исследования ассоциированных типов идентификатора в программе. Семантика использования макросов и семантика применения функций. Использование динамической памяти. Характеристика конструкторов и деструкторов.

Рубрика Программирование, компьютеры и кибернетика
Вид методичка
Язык русский
Дата добавления 31.01.2019
Размер файла 50,6 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Министерство общего и профессионального образования

Российской Федерации

Московский физико-технический институт

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

«Утверждаю»

Проректор института

Методические указания по курсу:

Вычислительная техника и программирование

ВВЕДЕНИЕ В ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++

А.С.Холодов

П.Н.Коротин

Москва 1997

УДК 681.32

Методические указания к практикуму по курсу: Вычислительная техника и программирование. Введение в объектно-ориентированное программирование на языке С++/ МФТИ. М., 1997. 76 с.

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

© Московский физико-технический институт, 1997

Язык программирования С был разработан в фирме Bell Laboratories Деннисом Ритчи (Dennis M. Ritchie). Первоначально он был реализован для операционной системы UNIX. В настоящее время С является одним из наиболее распространенных языков программирования и реализован для большинства ЭВМ и операционных систем. Одним из преимуществ языка С является то, что редко возникает необходимость в подпрограммах на языке ассемблера. Иногда С даже называют наиболее точной аппроксимацией мобильного языка ассемблера, доступного в настоящее время. В последнее время появилось достаточно книг, довольно подробно и широко обсуждающих особенности языка программирования С. Однако, как впрочем и большинство языков программирования, С постоянно развивается, обогащаясь новыми подходами и современными идеями. Программистам уже трудно ужиться в рамках языка С, что, видимо, и привело к появлению нового языка, надмножества языка С, называемого С++.

В отличие от остальных "собратьев" С++ не является диалектом языка С или вариацией на его тему. По существу, это новый язык программирования, основанный в значительной мере на языке С. С++, разработанный в начале 80-х годов также сотрудником Bell Laboratories Бьярном Страустрапом (Bjarne Stroustrup), представляет собой совместимое снизу вверх расширение языка программирования С, поддерживающее современную методологию построения больших программных комплексов.

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

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

В предлагаемой Вашему вниманию небольшой разработке основное внимание уделено описанию возможностей языка С++. Сведения о С приводятся, как правило, фрагментарно и связаны с прямым расширением языка. За более детальным изложением языка С следует обратиться к бо-лее толстым руководствам, таким, как, например:

Б.В.Керниган, Д.Ритчи, А.Фьюэр. Язык программирования Си. М.: Финансы и статистика, 1985;

Я. Белецкий. Энциклопедия языка Си. М.: Мир, 1992

или практически любому руководству по ОС UNIX.

Очень краткое (и потому затруднительное для первого чтения) руководство для имеющих практику работы с этим языком представляют

М.И.Болски. Язык программирования Си. Справочник. М.: Радио и связь, 1988;

Дерк Луис. С и С++. Справочник. М.: Бином, 1997.

В последнее время вышло очень много толстых книг по С++. Более полное изложение С++ можно найти, например, в

B.Stroustrup. The C++ Programming Language. Addison-Wesley, Reading, Mass., 1986;

I.Pohl. C++ for programmers. The Benjamin / Cummins Publishing Company, Inc. Redwood City, 1989;

Том Сван. Освоение Borland® C++ 5. Киев: Диалектика, 1996;

Тэд Фейсон. Объектно-ориентированное программирование на Borland® C++ 4.5. Киев: Диалектика, 1996;

Стивен Поттс, Тимоти С. Монк. Borland® C++ в примерах. Минск: Попури, 1996.

Подробнее в этой брошюре рассматриваются основные идеи, связанные с объектно-ориентированным программированием.

Прямые расширения

Прямые расширения языка С были введены в язык С++ с целью создания условий, затрудняющих появление наиболее типичных ошибок. Эти расширения включают в себя: изменения в механизме контроля и приведения типов аргументов функций, введение функций, представляющих собой открытые подстановки (inline), и литеральных констант, введение нового механизма передачи аргументов функциям с переменным числом аргументов и семантически новой возможности описания переменных.

Описания и константы

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

int i=1;

/* описание целой переменной i и присваивание ей начального значения 1 */

float a;

// вещественная переменная a

char s='a', str[10];

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

char* name="Mike";

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

struct complex { float re, im; };

/* описание структуры комплексного числа */

extern complex sqrt()

/* внешняя функция, возвращающая в качестве значения комплексное число */

Здесь, наряду с операторами, помещены комментарии. Два символа наклонной черты выделяют текст, являющийся комментарием. Концом комментария служит конец строки. Символы /* и */ выделяют комментарии, как это принято в С.

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

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

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

#define SIZE 6

float a[SIZE];

void clear()

{

for(int i=0; i<SIZE; i++) a[i]=0.;

}

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

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

const int SIZE=6; ,

а не ее подстановку.

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

int n=10;// глобальное n

void clear(int* b)

{

int n;// локальное n; глобальное имя n скрыто

for( n=0; n<::n; n++ ) b[n]=0;

}

int* ptr=&n;// присваивание указателю адреса глобального n

Возможности использовать скрытое локальное имя нет.

Имена параметров функции считаются описанными в самом внешнем блоке, поэтому

void f(int x)

{

int x;

}

содержит ошибку, т.к. имя x определено дважды в одной и той же области видимости.

Время жизни

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

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

Идентификаторы

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

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

При компиляции программы транслятор ищет наибольшую последовательность символов, которая может быть именем. Поэтому var10 - это одно имя, а не имя var, за которым следует число 10. Так же, как и elseif, - это одно имя, а не ключевое слово else, после которого стоит ключевое слово if.

Типы

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

В набор основных типов в С входят:

char

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

int

- целые значения. Допустимый диапазон зависит от конкретной системы (обычно от 32768 до +32767);

float

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

double

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

Существуют три модификатора, использующиеся для модификации основных типов:

short- для уменьшения размера памяти, отводимой под тип;

long- для увеличения размера отводимой под тип памяти;

unsigned- для представления без знаковых величин.

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

char

short int

int

long int

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

unsigned char

unsigned short int

unsigned int

unsigned long int

В комбинациях из нескольких слов спецификатор int можно опускать, что не меняет смысла:

const a = 1;

static x;

здесь все описания вводят объекты типа int.

Целый тип char наиболее удобен для хранения и обработки символов, обычно это 8-битовый байт. В зависимости от реализации char может быть как знаковым, так беззнаковым целым. Тип unsigned char, конечно, всегда беззнаковый, и при его использовании получаются более переносимые программы, но из-за применения его вместо просто char могут возникнуть значительные потери в эффективности.

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

Так, использование unsigned в качестве гарантии того, что целое значение положительно, опрометчиво из-за правил неявного преобразования. Например, вполне возможно

unsigned surprise = -1;

(хотя в этом случае компилятор и выдаст предупреждение) или

int a = 256+255;

char c = a;

int b = c; .

Переменная с будет содержать двоичный код 11111111 - "все едини-цы", который при присваивании его переменной b никак не может превра-титься в 511.

Все, что предполагается относительно типов, это:

1 == sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)

sizeof(float) <= sizeof(double)

Из основных типов могут выводится производные типы посредством операций описания:

*- указатель,[]- вектор,

&- ссылка,()- функция.

и механизма определения структур.

Например:

int* a;

float v[10];

char* p[20]; // вектор из 20 указателей на символ

void f(int); /* функция не возвращающая значения, аргументом

которой является целое */

struct string { short length; char* p; };

enum week_day { Sunday, Monday, Tuesday, Wednesday, Thursday,

Friday, Saturday };

struct compounent { week_day day; int i; };

compounent array[10]; .

Здесь в качестве иллюстраций введения новых типов приведены описания перечислимого типа week_day, который использован при определении типа compounent - структуры, содержащей переменную day, типа week_day и целую переменную i. В свою очередь этот тип используется при описании массива array. B языке C последние два описания должны были бы выглядеть так:

struct compounent { enum week_day day; int i; };

struct compounent array[10]; .

Этот пример иллюстрирует тот факт, что в С++ имена шаблонов трактуются как типы.

Перечислимые типы были введены в С еще в начале 80-х годов. В С++ перечислимые типы трактуются как целые переменные, как это принято в ANSI C. Когда имена такого типа перечисляются без начальных значений, список этих переменных неявно инициализируется последовательными целыми, начиная с нуля. Следующие два примера приводят эквивалентные описания:

а)

enum week_day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

enum week_day { Sunday=0, Monday=1, Tuesday=2, Wednesday=3, Thursday=4,Friday=5, Saturday=6 };

б)

enum week_day { Sunday=7, Monday,=1 Tuesday, Wednesday,

Thursday, Friday, Saturday };

enum week_day { Sunday=7, Monday=1, Tuesday=2, Wednesday=3, Thursday=4,Friday=5, Saturday=6 };

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

Пример

Ниже приводится функция, инициализирующая колоду из 52 карт.

enum suit { clubs, diamonds, hearts, spades };

struct card { suit s; int pips; };

card deck[52];

void init_deck(card d[])

{

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

d[i].s = (i/13); // перечислимое, как целое

d[i].pips = 1 + i%13;

}

}

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

Ключевое слово void используется для описания типа функций, не возвращающих значения, и/или пустого списка аргументов функций. Другое его назначение связано с использованием обобщенного указателя void*. Указателю такого типа может быть присвоена величина указателя любого типа. Но такой указатель не может быть разименован. (Операция разименовывания *, воздействуя на указатель объекта, делает возможным доступ к самому объекту.)

Пример

#include <stdio.h>

void memcpy(void* s1, const void* s2, unsigned n)

{

while (n--) *((char*)s1+n)=*((char*)s2+n);

}

void main(void)

{

char *s1="Первая строка";

char *s2="Вторая строка";

memcpy(s1,s2,4);

printf("%s\n",s1);

int i1=10;

int i2=20;

memcpy(&i1,&i2,sizeof(int));

printf("i1=%d\n",i1);

}

Результатом выполнения программы является

Вторая строка

i1=20

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

Встраевыемые функции (Inline-подстановки)

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

#define SQ(x) x*x

при следующем обращении к этому макросу

SQ(a+b)

породит код

a+b*a+b

Указанный макрос мог бы быть более правильно записан в виде

#define SQ(x) (x)*(x)

Однако второй пример показывает, что и этот рецепт тоже не универсален. Функция skipblanks, предназначавшаяся для удаления пробелов и символов табуляции, будет выполняться совсем не так, как ожидалось

#include <stdio.h>

#define toblank(c) ((c) != '\t' ? (c) : ' ')

int skipblanks()

{

int c;

while(toblank(c=getchar()) == ' ');

return c;

}

void main(void)

{

int c;

while( (c=skipblanks()) != EOF ) putchar(c);

}

И в результате обработки строки

a b c d e f g h

букв от a до h, разделенных двумя пробелами, получится строка

bdfh.

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

inline int toblank(int c) { return c != '\t' ? c : ' '; }.

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

Прототипы функций

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

Type name Function name () ;

Это описание анонсировало, что где-то описана функция с именем Function name, возвращающая в качестве результата значение указанного типа Type name. Никакой информации о типе и количестве аргументов функции не предполагалось. Наиболее часто встречающейся ошибкой в С было обращение к функции, например, имеющей вещественный аргумент, с целой переменной. Получаемый результат вычислений мог озадачить, но никаких сообщений об ошибках не поступало, т.к. контроль типов при вызове функций в С не предусмотрен.

В С++ сделан шаг в сторону жестко типизированных языков. Описание функции поэтому имеет вид

Type_name Function_name ( Arguments declaration list ) ;

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

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

Примеры

double sqrt(double);

void fun(char*, int);

void qsrt(void);

int printf(char* format,...); .

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

Пример

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

#include <stdio.h>

#include <stdarg.h>

int average( int first, ... );

void main()

{// Вызов с 3 целыми аргументами

//(-1 - используется как ограничитель).

printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );

// Вызов с 4 целыми аргументами.

printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );

// Вызов только с -1 - ограничителем.

printf( "Average is: %d\n", average( -1 ) );

}

/* Вычисление среднего значения от набора целых. */

int average( int first, ... )

{

int count = 0, sum = 0, i = first;

va_list marker;

va_start( marker, first ); //Инициализация переменных аргументов

while( i != -1 )

{

sum += i;

count++;

i = va_arg( marker, int );

}

va_end( marker );// Восстановление списка

return( sum ? (sum / count) : 0 );

}

Результат выполнения этой программы следующий:

Average is: 3

Average is: 8

Average is: 0

При обращении к функции

s=sqrt(4);

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

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

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

Перегружаемые функции

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

Пример

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

#include <stdio.h>

#include <stdlib.h>

inline void ERR(char* s1, char* s2="") { printf("%s %s\n",s1,s2); exit(1); }

inline void ERR(char* s1, int s2) { printf("%s %d\n",s1,s2); exit(1); }

void main(int argn, char** argv)

{

FILE *f;

int c,n,i;

if (argn<2) ERR("Укажите имя файла");

f=fopen(*(argv+1),"r");

if( f==NULL ) ERR(*(argv+1),"- файл не найден");

printf("Показать строку номер - ");

scanf("%d",&n); i=n;

if ( n<1 ) ERR("Номер строки должен быть больше нуля");

while( n && (c=fgetc(f)) != EOF ) if ( c=='\n' ) n--;

if ( n ) ERR("В файле нет строки номер ",i);

else while( (c=fgetc(f)) != EOF && c!='\n') putchar(c);

putchar('\n');

}

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

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

1. Не требуется никакого преобразования фактического аргумента или это преобразование является эквивалентным (к такого сорта преобразованиям относятся преобразования имени массива в указатель, имени функции в указатель на функцию, аргумента типа Т в тип const Т).

2. Преобразование является обобщающим, не приводящим к потере информации (например, преобразование из float в double, char в int, short в int и т.п.).

3. Требуется стандартное преобразование типа фактического аргумен-та, которое, вообще говоря, может привести и к потере информации (таким преобразованием являются преобразования из int в float и double, из float и double в int, из unsigned в int и т.п.).

4. Существует определенное пользователем преобразование типа фактического аргумента (с использованием конструкторов и операции преобразования типа см. стр. 41)

5. Фактическому аргументу соответствует параметр многоточие.

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

Пример

float abs( float );

double abs( double );

void main( void )

{

f( 5 );// ошибка; преобразования из int в float и

// из int в double имеют одинаковый приоритет

}

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

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

Ссылки

В С++ имеется возможность ссылаться на объекты. Описание вида

Type name &Identifier = Object ;

указывает на то, что имя Identifier есть другое имя объекта Object. Например, описания

int n;

int& nn=n;

double a[10];

double& last=a[9];

вводят имена nn и n для одного и того же целого объекта, и имя last для последнего элемента вещественного массива a.

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

Функция, предназначенная для упорядочения двух целых величин по возрастанию, записанная на С и использующая указатели и операцию разыменования:

#include <stdio.h>

void sequence(a,b)

int *a,*b;

{

int rab;

if( *a < *b ) { rab=*a; *a=*b; *b=rab; }

}

void main(void)

{

int a,b;

printf("Ведите два целых числа - ");

scanf("%d%d",&a,&b);

sequence(&a,&b);

}

могла бы на С++ выглядеть так:

#include <stdio.h>

void sequence(int& a,int& b)

{

if( a < b ) { int rab=a; a=b; b=rab; }

}

void main(void)

{

int a,b;

printf("Ведите два целых числа - ");

scanf("%d%d",&a,&b);

sequence(a,b);

printf(" Max - %d, Min - %d\n",a,b);

}

Использование динамической памяти

В С++ определены два унарных оператора, предназначенных для работы со свободной памятью. Они более удобны в использовании и заменяют определенные в С библиотечные функции malloc, calloc и free. Оператор new может быть записан в виде

new Type name ;

new Type name Initializer ;

new (Type name) ; .

В любом случае в результате его выполнения, во-первых, захватывает-ся подходящий для размещения Type name кусок свободной памяти, и, во-вторых, адрес начала этого пространства памяти возвращается в качестве значения оператора. Возвращаемое оператором new значение типа void* может быть присвоено указателю любого типа. Использование оператора new с инициализацией относится к прерогативе классов и обсуждается ниже.

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

delete Expression ;

delete [ Expression ] Expression ; .

Обычно Expression - это указатель, которому присвоено значение оператором new. Наиболее общая первая форма записи оператора, вторая - используется, когда освобождаемая память выделялась типом массив. В этом случае выражение в скобках задает количество элементов массива. Оператор delete не возвращает никакого значения.

Пример

#include <stdio.h>

void main( void )

{

int size;

printf("Укажите размер массива - ");

scanf("%d",&size);

int* data = new int[size];

for ( int j=0, j<size; j++ ) data[j]=j;

delete data;

}

Потоки

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

cin- для ввода информации,

cout- для вывода информации,

cerr- для вывода информации об ошибках.

Для помещения информации в поток cout или cerr служит операция <<, а для получения ее из потока cin - >>.

Например, с использованием потоков программа, приведенная в примере на стр. 20, могла бы выглядеть так:

#include <iostream.h>

#include <stdio.h>

#include <stdlib.h>

inline void ERR(char* s1, char* s2="") { cerr << s1 << " " << s2 << "\n"; exit(1); }

inline void ERR(char* s1, int s2) { cerr << s1 << " " << s2 << "\n"; exit(1); }

void main(int argn, char** argv)

{

FILE *f;

int c,n,i;

if (argn<2) ERR("Укажите имя файла");

f=fopen(*(argv+1),"r");

if( f==NULL ) ERR(*(argv+1),"- файл не найден");

cout << "Показать строку номер - ";

cin >> n; i=n;

if ( n<1 ) ERR("Номер строки должен быть больше нуля");

while( n && (c=fgetc(f)) != EOF ) if ( c=='\n' ) n--;

if ( n ) ERR("В файле нет строки номер ",i);

else while( (c=fgetc(f)) != EOF && c!='\n') cout << c;

cout << "\n";

}

Абстракция данных

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

Структуры

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

Конструкция формы

Structure_variable.Member_name

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

Пример описания массива структур и инициализации компонент структуры можно найти на стр.14.

Если определен указатель на структуру Pointer to structure, то для доступа к члену Member name может быть выбрана одна из форм

Pointer to structure --> Member name

или

(*Pointer to structure).Member name

Например, присвоить начальные значения массиву комплексных чисел можно было бы так:

#include <iostream.h>

const n=10;

struct complex { float re,im; };

void main(void)

{

complex array[n], *ptr=array;

for ( int i=0; i<n; i++ ) { ptr-->re=i; (*ptr++).im=2.; }

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

cout << "(" << array[i].re << "," << array[i].im << ")\n";

}

Операторы --> и "." наряду с операторами [] и () имеют наивысший приоритет и выполняются слева направо.

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

void assign(complex num, float re, float im)

{

num.re = re;

num.im = im;

}

void print(complex num)

{

cout << "(" << num.re << "," << num.im << ")\n";

}

Но элегантнее на С++ то же самое могло бы быть записано, когда последние две функции являются членами структуры:

#include <iostream.h>

const n = 10;

struct complex

{

float re, im;

void assign ( float r, float i ) { re = r; im = i; }

void print();

};

void complex::print() { cout << "(" << re << "," << im << ")\n"; }

void main(void)

{

complex array[n], *ptr = array;

for ( int i=0; i<n; i++ ) { ptr++-->assign(i,2.); }

for ( i=0; i<n; i++ ) array[i].print();

}

Функции assign и print, являющиеся членами структуры complex, имеют прямой доступ к полям структуры re и im. Обращение к такого рода функции имеет ту же семантику, что и обращение к компонентам структуры. Собственно в описании структуры complex помещен лишь прототип функции print. В определение функции-члена print, вынесенное за рамки описания структуры, с помощью оператора расширения области видимости помещается указание на то, что эта функция относится к структуре complex.

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

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

Таким образом, описание структуры complex могло бы быть и таким:

struct complex

{

private:

float re,im;

public:

void assign (float, float);

void print();

};

Пример

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

#include <iostream.h>

#include <stdlib.h>

inline void ERR(char* s1) { cerr << s1 << "\n"; exit(1); }

const max_len=10;

struct Stack {

private:

int i;

char s[max_len];

public:

void ini() { i=0; }

void put(char c) { if(++i==max_len) ERR("Stack is full"); else s[i]=c; }

char get() { if (i) return s[i--]; else ERR("Stack is empty"); }

int top() { return i; }

};

void main(void)

{

char* s="abcdefgh";

Stack stack;

stack.ini();

cout << s << "\n";

while( *s ) stack.put(*s++);

while( stack.top() ) { cout << stack.get(); }

cout << "\n";

}

Результатом ее выполнения являются две строки:

abcdefgh

hgfedcba

Классы

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

Описание структуры Stack поэтому могло бы выглядеть и так:

class Stack {

int i;

char s[max_len];

public:

void ini();

void put(char);

char get();

int top();

};

Для описания в классе переменных не могут быть использованы модификаторы auto, register, extern, тем не менее возможно использовать модификатор static. Такие переменные создаются один раз и постоянно находятся в памяти, играя роль общей памяти класса. Как обычно, воспользоваться ими можно, прибегнув к оператору расширения области видимости:

Class_name :: identifier ,

если только они имеют статус public.

Пример

enum boolean { false, true };

class str {

char s[100];

public:

static boolean read_only;

void print();

void assign(char*);

};

str::read_only = false;

void main(void)

{

str s1, s2;

if ( !str::read_only ) s1.assign("Example");

}

Конструкторы и деструкторы

Конструктором называют функцию-член класса, имеющую то же имя, что и класс. Как правило, конструкторы используются для инициализации переменных, являющихся членами класса, и иногда для выделения свободной памяти с помощью оператора new. Деструктором называют функцию, имеющую имя, состоящее из символа "~", за которым идет имя класса. Обычно деструкторы используются для уничтожения с помощью оператора delete объектов класса, расположенных в свободной памяти.

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

Пример

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

#include <iostream.h>

#include <stdlib.h>

#include <string.h>

#include <alloc.h>

inline void ERR ( char* str ) { cerr << str << "\n"; exit(1); }

class Stack {

int size;

char* top;

char* s;

public:

Stack ( int ) ;

Stack ( char* ) ;

~Stack () ;

void put ( char ) ;

char get () ;

int istop () ;

};

Stack :: Stack ( int sz )

{

top = s = new char[ size=sz ];

}

Stack :: Stack ( char* str )

{

top = s = new char[ size=strlen(str) ];

while ( *str ) *s++=*str++;

}

Stack :: ~Stack ()

{

delete top;

}

void Stack :: put ( char c )

{

if( top+size==s ) ERR("Stack is full");

*s++ = c;

}

char Stack :: get ()

{

if ( top==s ) ERR("Stack is empty");

return *--s;

}

int Stack :: istop ()

{

return s == top ? 0 : 1;

}

const n = 12;

void main ( void )

{

{

Stack a ( n ) ;

Stack b ( "Hello, Dolly!" ) ;

// Можно и так Stack b = "Hello, Dolly!";

for ( int i='a'; i<'a'+n; i++ ) a.put(i);

for ( i=0; i<n; i++ ) cout << a.get();

cout << "\n";

while( b.istop() ) cout << b.get();

cout << "\n";

cout << "Свободно " << coreleft() << " байт памяти\n";

}

cout << "Свободно " << coreleft() << " байт памяти\n";

}

Результат выполнения программы:

lkjihgfedcba

!ylloD ,olleH

Свободно 61008 байт памяти

Свободно 61040 байт памяти

При описании объектов a и b класса Stack использовались два различных конструктора, причем при описании объекта b он был также инициализирован начальным значением. Начальное значение объекту a присваивается при выполнении программы, а на этапе описания определяется только его размер. Увеличение объема доступной свободной памяти показывает, что при выходе из блока, содержащего объекты a и b, динамически захваченная память была освобождена, т.е. неявно произошло обращение к деструктору класса Stack.

Описание объекта без параметров

Stack c;

привело бы к ошибке, т.к. конструктор с пустым списком параметров в описании класса Stack не указан.

Указатель this

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

Пример

Каждое слово копируется в объект класса item. Указатель на первый объект класса помещается в вершину списка, следующие объекты размещаются в списке в порядке, определяемом первой буквой каждого слова.

#include <iostream.h>

const n = 10;

class item {

char a[n] ; // слово

item* next ; // ссылка на следующий элемент

static item* top ; // вершина списка

public:

item ( char* ) ;

char* getvalue () { return a; }

item* getnext () { return next; }

void insert ( item* s ) { next = s; }

void print () ;

};

item :: item ( char* str )

{

char* ptr = a;

while ( *ptr++ = *str++ );

/* заполнить пустой список */

if ( !top ) { top=this; next=NULL; }

/* проверить вершину списка */

else if ( a[0] < *top-->getvalue() ) { next=top; top=this; }

/* найти нужное место внутри или в конце списка */

else {

item* ptr = top;

while ( ptr-->getnext() && a[0] > *ptr-->getnext()-->getvalue() )

ptr=ptr-->getnext();

next = ptr-->getnext();

ptr-->insert ( this ); }

}

void item::print()

{

item* ptr=top;

while ( ptr ) { cout < ptr-->getvalue() < "\n";

ptr=ptr-->getnext(); }

}

item* item::top=NULL;

void main(void)

{

item* ptr = new item("Zero");

ptr = new item("Hello");

ptr = new item("Allo");

ptr = new item("Bella");

ptr = new item("Cent");

ptr-->print();

}

Результатом выполнения программы является

Allo

Bella

Cent

Hello

Zero

Вершина списка размещается в статической переменной, что делает ее доступной для всех объектов класса item.

Определение статических членов класса

В приведенной выше программе встречается строка

item* item::top=NULL; ,

которая обычно вызывает некоторое недоумение.

Дело в том, что описание статической переменной top в классе item рас¬сматривается транслятором всего лишь как описание, т.е. транслятор не отводит памяти под статические члены класса и не обеспечивает их инициализацию. Это является изменением первоначального определения статических членов С++, которое предполагало их неявное определение и инициализацию нулем. От этого пришлось отказаться в силу того, что не все объекты различных типов могут или должны инициализироваться подобным образом. Например, после того, как пользователем определены какие-либо классы с конструкторами, на их основе возможно описание статических членов нового класса. Поэтому под статические члены класса необходимо явным образом выделить память и их инициализировать. Указанная выше строка именно для этих целей и присутствует в программе.

Статические функции члены класса

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

Пример

#include <iostream.h>

#include <math.h>

class complex {

double re, im ;

public:

complex(double r, double i) { re = r; im = i; }

static void print( complex& );

void print() { cout << "(" << re << "," << im << ")\n"; }

static double mod( complex& a ) { return sqrt( a.re*a.re + a.im*a.im ); }

};

void complex::print( complex& a )

{

cout << "{" << a.re << "," << a.im << "}\n";

}

void main( void )

{

complex a(1.,2.), b(3.,4.);

cout << "Обращение к обычной функции: "; a.print();

cout << "Обращение к статической функции: "; a.print(b);

cout << "Модуль числа: " << complex::mod(b) << "\n";

// возможно и так

}

Результат выполнения:

Обращение к обычной функции: (1,2)

Обращение к статической функции: {3,4}

Модуль числа: 5

Константные функции члены класса

Рассмотрим программу

class complex {

double re, im ;

public:

complex(double r, double i) { re = r; im = i; }

void print();

};

void complex::print()

{

cout << "(" << re << "," << im << ")\n";

}

void main( void )

{

const complex a(1.,2.);

a.print();

}

При компиляции этой безобидной, на первый взгляд, программы транслятор выдаст предупреждение об ошибке. У него нет уверенности в том, что функция print не попытается изменить объект a, который, по его определению, должен быть неизменным. Поэтому все функции относительно, которых предполагается возможность их работы с объектами типа const, должны иметь особые полномочия на этот случай. Сообщение о таких возможностях функции дает слово const, которое указывается в заголовке функции. Такие функции имеют доступ к объектам класса любого вида (как к обычным, так и к константным).

В нашем случае описание класса выглядело бы так:

class complex {

double re, im ;

public:

complex ( double, double );

void print() const ;

};

Указатели на функции

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

#include <stdio.h>

f(a) int a; { return a*a; }

void main()

{int (*pf)() = f;

printf("%d\n",(*pf)(5));

}

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

#include <iostream.h>

int f(int a) { return a*a; }

void main( void )

{int (*pf)(int) = f;

cout << (*pf)(5) << "\n";

}

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

Например, для константной функции print класса complex описание и использование указателя могло бы выглядеть так:

class complex {

double re, im ;

public:

complex ( double, double );

void print() const ;

};

void main( void )

{const complex a(1.,2.), *b = &a ;

void (complex::*pmf)() const = complex::print;

(a.*pmf)();

(b-->*pmf)();

}

Как заметил Страустрап: "Синтаксис на вид не слишком привлекателен, но о синтаксисе С, который служил образцом, этого тоже не скажешь".

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

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

Членами класса могут быть также объекты другого класса. Например, если определен класс vect

class vect {

float* a;

int sz;

public:

vect ( int );

};

то класс mvect, состоящий из трех векторов, мог бы быть описан так:

class mvect {

vect a, b, c;

pulic:

mvect( int sz ) : a(sz), b(sz), c(sz) {}

};

Необычным кажется описание конструктора класса mvect. В данном случае тело конструктора состоит из пустого блока, однако в заголовке (после двоеточия) указывается, что должен быть вызван конструктор класса vect для каждого из элементов a, b и c. Обращение к конструкторам производится в порядке написания слева направо, т.е. сначала - для a, и в последнюю очередь - для c.

Преобразование типов

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

int i = 2; double s = 3.14; int j = s + i;

при вычислении j значение переменной i будет неявно преобразовано к типу double, а затем результат вычисления суммы, преобразованный к ти-пу int, будет присвоен переменной j.

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

j = s + (double)i;

в С++ то же самое могло бы быть записано также в виде

j = s + double(i);

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

p = (int*)q;

в то время как запись

p = int*(q);

приведет к ошибке.

До сих пор шла речь о преобразовании друг в друга основных типов. Куда более актуален вопрос о преобразовании типов, определенных пользователем.

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

Например, для класса

class complex {

double re, im;

publc:

complex() { re = im = 0.; }

complex( double r, double i = 0 ) { re = r; im = i; }

};

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

complex a;

a = complex(2.0); ,

так и неявно

complex a;

a = 5.;

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

Operator Type() { . . . }

Здесь Type - тип, по отношению к которому определяется преобразование пользовательского типа.

Например, для следующим образом определенного типа complex:

class complex {

double re, im;

public:

complex() { re = im = 0; }

complex( double r, double i = 0 ) { re = r; im = i; }

operator double() { return sqrt( re*re + im*im ); }

void print() { cout << re << "+" << im << "i"; }

};

результат выполнения программы

complex greater( complex a, complex b )

{

return a > b ? a : b;

}

void main ( void )

{

double m;

complex a(3,4), b, max;

b = 4;

m = a;

cout << "Модуль комплексного числа ";

a.print();

cout << " равен " << m << "\n";

max = greater( a, b );

cout << " Большее число - "; max.print();

cout << "\n";

}

такой

Модуль комплексного числа 3+4i равен 5

Большее число - 3+4i

В функции greater производится сравнение двух вещественных чисел, к которым неявно преобразуются комплексные числа a и b, а в качестве результата возвращается комплексное число с большим модулем.

Дружественные функции

Для повышения эффективности работы программы, часто использующиеся функции, было бы разумно сделать членами класса. Но нередко на практике встречаются ситуации, когда такие функции должны работать с объектами различных классов. Например, если определены два класса: вектор и матрица, то функция умножения матрицы на вектор должна эффективно работать как с тем, так и с другим классом. Именно для случаев, когда функция должна была бы быть членом нескольких классов, используется механизм дружественных функций. Функция, друг класса, имеет те же права по отношению к данным, что и член класса. Ее прототип должен быть указан в описании класса с префиксом friend. В то же время функция может быть дружественной по отношению к нескольким классам.

Пример

#include <iostream.h>

#include <stdlib.h>

inline void ERR(char* s,int i) { cerr << s << " " << i << "\n"; exit(1); }

inline void ERR(char* s) { cerr << s << " " << "\n"; exit(1); }

class matrix;

class vect {

float* p;

int size;

public:

vect ( int );

float& get_item ( int );

void print();

friend vect mvm(matrix& m, vect& v);

~vect();идентификатор макрос память деструктор

};

vect :: vect( int n )

{

if ( n < 0 ) ERR("Ошибка в размере вектора",n);

p = new float [ size = n ];

}

float& vect :: get_item( int n )

{

if ( n < 1 || n > size ) ERR("Ошибка в номере элемента вектора",n);

return p[n-1];

}

void vect :: print()

{

for ( int i=0; i<size; i++ ) cout << "\t" << p[i] << "\n";

}

vect :: ~vect()

{

delete p;

}

class matrix {

float** p;

int size1, size2;

public:

matrix( int, int );

float& get_item ( int, int );

void print();

friend vect mvm( matrix& m, vect& v );

~matrix();

};

matrix :: matrix( int n1, int n2 )

{

if (n1 < 0) ERR("Ошибка в первой размерности матрицы",n1);

if (n2 < 0) ERR("Ошибка во второй размерности матрицы",n2);

p = new float* [ size1 = n1 ];

for ( int i=0; i<n1; i++ ) p[i] = new float [ n2 ];

size2 = n2;

}

float& matrix :: get_item( int n1, int n2 )

{

if ( n1 < 1 || n1 > size1 )

ERR("Ошибка в первом номере элемента матрицы",n1);

if ( n2 < 1 || n2 > size2 )

ERR("Ошибка во втором номере элемента вектора",n2);

return ( p[n1-1][n2-1] );

}

void matrix :: print()

{

int i, j;

for( i=0; i<size1; i++ ) { cout << "\t";

for( j=0; j<size2; j++ ) cout << p[i][j] << " ";

cout << "\n"; }

}

matrix :: ~matrix()

{

for( int i=0; i<size1; i++ ) delete p[i];

delete p;

}

vect mvm( matrix& m, vect& v )

{

if( m.size2 != v.size )

ERR("Ошибка: различные размерности матрицы и вектора");

vect r(m.size1);

for (int i=0; i<m.size1; i++ ) {

r.p[i] = 0.;

for ( int j=0; j<v.size; j++ ) r.p[i] += m.p[i][j]*v.p[j]; }

return r;

}

void main( void )

{

vect a(2), c(3);

matrix b(3,2);

a.get_item(1) = 1.; a.get_item(2) = 2.;

b.get_item(1,1) = 1.; b.get_item(1,2) = 2.;


Подобные документы

  • Использование скриптового языка программирования для разработки web-приложений (сценариев). Изучение основ объектно-ориентированного программирования в языке PHP. Ознакомление со специальными методами для работы с классами. Назначение интерфейсов.

    контрольная работа [25,1 K], добавлен 14.03.2015

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

    презентация [214,9 K], добавлен 26.10.2013

  • Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.

    учебное пособие [1,6 M], добавлен 28.12.2013

  • Свойства объектно-ориентированного языка программирования. Понятия инкапсуляции и наследования. Виртуальные функции и полиморфизм. Инициализация экземпляра объекта с помощью конструктора. Динамическое создание объектов. Совместимость объектных типов.

    реферат [17,0 K], добавлен 15.04.2015

  • Разработка программы с использованием принципов объектно-ориентированного программирования на языке высокого уровня С средствами Microsoft Visual Studio 2010. Построение алгоритма реализации. Класс программы, инструкция по использованию программы.

    курсовая работа [1,0 M], добавлен 26.12.2013

  • Почему C++. Возникновение и эволюция языка C++. Сравнение языков С++ и С. Эффективность и структура. Процедурное программирование. Модульное программирование. Абстракция данных. Объектно-ориентированное программирование. Улучшенный С.

    реферат [26,4 K], добавлен 03.06.2004

  • Введение в объектно-ориентированное программирование. Постановка задачи. Описание алгоритма решения в псевдокоде (команды в виде текста на русском языке). Исходный текст программы на С. Тестирование программы. Модификация программы. Полиморфизм.

    курсовая работа [294,0 K], добавлен 08.09.2008

  • Описание предметной области. Контроль и методы доступа. Работа с графикой в С++ Builder. Программирование игры "Воздушный бой" с использованием основных принципов объектно-ориентированного программирования. Принципы работы конструкторов и деструкторов.

    курсовая работа [901,0 K], добавлен 31.05.2015

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

    контрольная работа [614,7 K], добавлен 16.09.2012

  • Принципы объектного подхода. UML как воплощение идеи визуального моделирования. Синтаксис и семантика основных объектов UML, различные отношения между классами. Диаграммы использования, их назначение. Разработка модели бизнес-прецедентов данных.

    презентация [1,0 M], добавлен 19.09.2016

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.