Объектно-ориентированное программирование

История развития C++ для операционных систем. Его графика и пользовательские интерфейсы. Объектно-ориентированное программирование. Бинарные и унарные операторы. Правила определения операторных функций. Использование языка UML и диаграммы классов.

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

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

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

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

1. История развития C++.

(Язык C был спроектирован Деннисом Ритчи в начале 70-х для создания UNIX).

Создатель C++ - Бьерн Страуструп (Bjarne Stroustrup), AT&T.

1980 г. «C с классами»

1998. Ратифицирован стандарт C++

Нововведение

Источник нововведения

Основные средства

C

Концепция классов (c производными классами и виртуальными функциями)

Simula67

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

Algol68

Шаблоны

Ada, параметризованные модули в Clu

Механизм обработки исключений

Ada, Clu, ML

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

Опыт использования C++

2. Использование C++

На C++ написаны операционные системы (полностью или частично).

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

Малый объем требуемой памяти.

Связь с ассемблером.

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

На C++ решается множество численных, научных и инженерных задач. (Причина этого в том, что традиционные численные задачи должны совмещаться с графикой и вычислениями, связанными со структурами данных, которые не укладываются в традиционный Фортран.)

Графика и пользовательские интерфейсы - области интенсивного использования C++.

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

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

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

C++ широко используется для обучения и исследований (моделирующие программы), т.к. он:

Реализация C++:

Для PC, Windows: Microsoft Visual C++ 6.0, 7.0, Borland C++.

Для UNIX-систем это - системный компилятор GNU v3.XX.

3. ООП

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

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

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

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

Для работы с этими данными требуются специальные функции (напрмер переместить, повернуть, сменить цвет, прочертить). Они помещаются вместе с данными в модуль.

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

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

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

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

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

Класс является типом данных, определяемым пользователем. Тип задает внутреннее представление данных в памяти компьютера, множество значений, которое могут принимать величины этого типа, а также операции и функции, применяемые к этим величинам. Все это можно задать и в классе. Например, тип int определяет, что величина этого типа (int i) занимает 4 байта, принимает значения от -231 до 231-1 и ее можно складывать, вычитать, и т.д. Создаваемый тип данных (квадрат) определяет, что объект этого типа (конкретный квадрат) будет занимать столько байт, сколько необходимо для размещения отдельных данных (положение, размер, ориентация (угол поворота), цвет). Размещение и размер квадрата ограничены пределами экрана, угол поворота - от 0 до 90 градусов. Квадрат можно переместить, повернуть, сменить цвет, прочертить.

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

Объектно-ориентированное программирование (ООП) - основная методология программирования 90-х. Она берет начало в Simula 67 и продолжается в (ранних) Smalltalk, LISP, Clu и (более поздних) Actor, Eiffel, Objective C, Java и C++.

[Буч]: ООП - это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.

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

ООП использует в качестве базовых элементов объекты, а не алгоритмы;

каждый объект является экземпляром какого-либо определенного класса;

классы организованы иерархически.

Концепции ООП

Моделирование действий из реального мира

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

Сокрытие деталей реализации

Возможность многократного использования кода благодаря наследованию

Интерпретация вызовов функций на этапе выполнения

Инкапсуляция - маскировка всех внутренних деталей, не влияющих на внешнее поведение.)

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

Inheritance - наследование.

Фазы трансляции

Программа на C++ состоит из:

одного или нескольких исходных файлов (с расширением .cpp);

нуля и более заголовочных файлов (с расширением .h).

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

Трансляция включает в себя три фазы (см. рис. 2):

а) фаза препроцессирования. Каждый исходный файл вместе со включенным в него (с помощью директивы #include) текстом из заголовочных файлов образует так называемую единицу трансляции. Также обрабатываются другие директивы препроцессора;

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

в) фаза компоновки. Объектные файлы объединяются в исполнимый модуль.

Рис.1. Трансляция программы на C++

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

Препроцессор

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

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

Директива макроопределения

#define идентиф замещающий_текст

#define идентиф(идентиф, идентиф, …, идентиф) замещающий_текст

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

#define IDI_ICON1 101

#define HIWORD(l) ((WORD)(((DWORD)(l) >> 16) & 0xFFFF))

#define CASE break; case

#define FOREVER for(;;)

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

Директива #include.

#include "path-spec"

#include <path-spec>

Директивы включения могут быть вложенными (до 10 уровней).

#undef идентиф

#pragma токен-стринг #pragma vtordisp( off )

Область видимости (scope)

Каждое имя (не только имя переменной) в C++ может использоваться только в определенной области программы. Эта область называется областью видимости имени.

Существует 5 областей видимости: локальная о.в., о.в. функции, о.в. файла, о.в. класса, о.в. прототипа.

Локальная область видимости. Имя, объявленное внутри блока, доступно только в этом блоке и в содержащихся в нем блоках, и только после точки объявления. Замещение имен.

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

Область видимости файла. Любое имя, объявленное вне какого бы то ни было блока или класса, имеет о.в. файла. Имя доступно после точки объявления до конца файла. Доступ через унарный оператор ::i

Область видимости класса. Имена членов класса имеют о.в. класса. Доступ через объект (. ->) и через бинарный оператор classname::i.

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

Связывание (linkage)

Программа состоит из нескольких единиц трансляции, связываемых вместе. Способ, которым имена объектов и функций совместно используются в различных ед. трансляции, называется связыванием. Существует 3 типа связывания: внутреннее (static int a;), внешнее (int a; extern int a;) и отсутствие связывания (typedef, enum).

Виды переменных по типу выделяемой памяти (способы использования памяти)

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

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

Свободная (динамическая) память, которую явно требует программа при размещении объектов, и которую она может освободить после того, как память больше не нужна (при помощи new и delete).

Класс памяти (storage class)

Класс памяти управляет продолжительностью жизни и связыванием объектов (переменных) в C++. (auto, register, static и extern)

Свободная память

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

Оператор new принимает следующие формы:

new имя_типа

new имя_типа инициализатор

new имя_типа [выражение]

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

int *p, *q;

p=new int(5);

q=new int[10];

delete выражение

delete [] выражение

Структуры

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

Структура - набор элементов произвольных типов. Элемент структуры называется членом.

struct human{

char *name;

int age;

}; //точка с запятой после '}'

void f(){

human vova;

vova.name="Vova";

vova.age=70;

human piter={"Петя",100};

human *pp=&vova;

pp->age+=1;

}

Имя структуры (human) называется теговым именем и является типом. Можно объявлять переменные типа human (vova, piter). Их можно присваивать, передавать в качестве аргументов и возвращать в качестве значения функции.

Инициализация структуры (см.прог.)

Для доступа к членам структуры используется оператор «точка» - «.» (vova.name). Конструкция вида

переменная_структуры.имя_члена

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

Оператор указателя структуры. К объектам типа структуры часто обращаются посредством указателей с помощью оператора «->» :

указатель_на_структуру->имя_члена

Это эквивалентно

(*указатель_на_структуру).имя_члена

Структура - тип данных, определяемый пользователем.

Определим тип стека.

const int max_len=1000;

struct stack{

int top;

char s[max_len];

};

void reset(stack *stk){stk->top=0;}

void push(stack *stk, char c){stk->s[stk->top++]=c;}

char pop(stack *stk){return stk->s[--stk->top];}

bool is_empty(const stack *stk){return stk->top==0;}

bool is_full(const stack *stk){return stk->top==max_len;}

void use_stack()

{

stack s;

char str[]="Using Stack!!!";

int i=0;

cout<<str<<endl;

reset(&s);

while(str[i]) push(&s,str[i++]);

while(!is_empty(&s))cout<<pop(&s);

cout<<endl;

// pop(&s);

}

Напечатается строка в прямом и обратном порядках.

КЛАССЫ

В C++ существуют два вида типов: встроенные и типы классов. Встроенные типы включают в себя: char, int, double. Типы классов включают в себя, например: string, vector, istream, CFile, CDialog.

Свои типы классов может создавать и программист. Если они хорошо написаны, то их также легко использовать, как и встроенные типы.

Класс применяется для определения типов, соответствующих концепциям приложения.

Функции-члены класса

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

Перепишем стек: (сначала определим функции в пределах struct)

struct stack2

{

enum{ max_len=100};

int top;

char s[max_len];

void reset();

void push(char c);

char pop();

bool is_empty()const;

bool is_full()const;

};

void stack2::reset() {top=0;}

void stack2::push(char c){s[top++]=c;}

char stack2::pop(){return s[--top];}

bool stack2::is_empty()const {return top==0;}

bool stack2::is_full()const {return top==max_len;}

Эти функции отличаются тем, что могут обращаться к именам членов класса (top, s) «как есть», непосредственно.

void use_stack2()

{

stack2 s;

char str[]="Using Stack!!!";

int i=0;

cout<<str<<endl;

s.reset();

while(str[i])

s.push(str[i++]);

while(!s.is_empty())

cout<<s.pop();

cout<<endl;

// pop(&s);

}

При вызове функций-членов применительно к конкретному объекту типа stack2 они действуют на указанные члены именно этого (своего) объекта: операционный программирование пользовательский интерфейс

stack2 data, operands;//создаются 2 отдельных объекта со своими

//top и s[]

//но функции не отдельные, а одни и те же

data.reset();//data.top=0;

operands.reset();//operands.top=0;

stack2 *pstack=&operands;

pstack->push('A');//operands.s[operands.top++]='A'

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

< Определение stack2, как написано выше >

Используется оператор разрешения области видимости “::” .

Управление доступом

Понятие структуры расширено в С++ так, что появилась возможность вводить закрытые (private) и открытые (public) члены. Это касается как членов данных, так и функций-членов структуры.

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

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

struct primer{

public:

int a; //public member

double b(int); //public member

private:

int c; //private member

double d(int); //private member

};

double primer::b(int p)

{

a=7; //ok, can access all members

b(3); //ok

c=2; //ok

return d(1); //ok

}

double primer::d(int p)

{

a=7; //ok, can access all members

b(3); //ok

c=2; //ok

return d(1); //ok

}

double use(int p)

{

primer pr;

pr.a=7; //ok, public member

pr.b(3); //ok, public member

// pr.c=2; //error: cannot access private member

// return pr.d(1);//error: cannot access private member

return 1;

}

Переменная primer::c может быть изменена с помощью вызова функции primer::b(), но не непосредственно.

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

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

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

class a{

int b;//private

}

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

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

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

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

class stack3

{

public:

stack3(); //конструктор по умолчанию

stack3(char);//stack that contains 1 element

void push(char c);

char pop();

bool is_empty()const;

bool is_full()const;

private:

enum{ max_len=100};

int top;

char s[max_len];

};

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

void use_stack3()

{

stack3 s; //выполняется конструктор по умолчанию

stack3 s1('d');//выполняется конструктор с одним аргументом

stack3 *ps=new stack3();//выполняется конструктор по умолч.

stack3 *ps1=new stack3('d');//выполн. конструктор c 1 арг.

delete ps;

delete ps1;

}

Определение конструктора

stack3::stack3():top(0){}

stack3::stack3(char c)//stack that contains 1 element

:top(1)

{

s[0]=c;

}

В определении конструктора используется новый для вас синтаксис. Между символом «:» и открывающей фигурной скобкой ({) находится ряд инициализаторов конструктора (constructor initializers), в данном случае 1 инициализатор. Инициализаторы конструктора велят компилятору инициализировать заданные члены значениями, указанными в соответствующих круглых скобках. В частности, top устанавливается равным 0. Второй конструктор иниц.1 и выполняет присваивание.

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

1. C++ - среда выделяет память для хранения объекта.

2. C++ - среда инициализирует объект в соответствии со списком инициализации конструктора.

3. C++ - среда выполняет тело конструктора.

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

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

Пример с 2 инициализаторами конструктора.

class complex{

complex(double real, double imag);

complex(double real);

};

complex::complex(double real, double imag)

{re=real; im=imag;}

complex::complex(double real)

{re=real; im=0;}

Использование:

complex c3(2,3); //инициализация

Конструктор не имеет возвращаемого значения (даже void).

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

Деструкторы

Некоторым классам требуется функция, которая гарантированно вызывается при уничтожении объекта (для закрытия файла, снятия блокировки и т.д.). Такие функции называются деструкторами. Имя деструктора - это имя конструктора с предшествующим символом «тильда»:

~stack3(){delete[] s};

Статические члены

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

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

имя_класса :: идентификатор

Здесь используется оператор разрешения области видимости.

Пример:

<сначала how_many_,

затем how_many(),

затем const max_len_>

//stat.h

class stack_stat

{

public:

stack_stat();

~stack_stat();

...

bool is_full()const;

static int how_many();

private:

static const int max_len_=100;

int top;

char s[max_len_];

static int how_many_;

};

//stat.cpp

#include "stat.h"

#include <cassert>

int stack_stat::how_many_=0;

stack_stat::stack_stat():top(0){++how_many_;}

stack_stat::~stack_stat(){--how_many_;}

...

bool stack_stat::is_full()const {return top==max_len_;}

int stack_stat::how_many(){return how_many_;}

//use.cpp

#include "stat.h"

#include <iostream>

using namespace std;

void use_stack_stat()

{

stack_stat *ps=new stack_stat();

stack_stat s;

// cout<<stack_stat::max_len()<<'\n';

cout<<stack_stat::how_many();

// ps->~stack_stat();

char str[]="Using Stack!!!";

int i=0;

cout<<str<<endl;

while(str[i])

ps->push(str[i++]);

//*ps=reverse_order(*ps);

while(!ps->is_empty())

cout<<ps->pop();

cout<<endl;

// s.pop();

delete ps;

cout<<stack_stat::how_many();

}

В этом примере в переменной how_many отслеживается, сколько объектов класса stack_stat было создано.

Функции-члены типа static и const

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

<пример с stat1>

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

<Пример из stack3>

Обычная функция-член может и читать, и изменять члены данных объекта.

Константная функция-член может читать, но не может изменять члены данных объекта. То есть такая функция не изменяет состояние объекта.

Статической функции-члену недоступны члены данных объекта, но доступны статические члены класса.

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

class complex{

double re,im;

public:

double real()const{return re;}

double real_bad_version(){return re;}

}

void f(){

const complex c1(1,0);

complex cx(2,3);

cout<<c1.real()<<cx.real();//ok

cout<<c1.real_bad_version();//не скомпилируется

cout<<cx.real_bad_version();//ok

}

Эффективные типы, определяемые пользователем

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

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

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

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

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

Перегруженные операторы - функции, обеспечивающие привычную (удобную) форму записи.

class complex{

public:

complex(double re=0,double im=0)

:_re(re),_im(im){}

double real()const{return _re;}

double imag()const{return _im;}

void add(complex);

private:

double _re,_im;

};

complex plus(complex a, complex b);

void print(complex a);

bool operator==(complex a, complex b);//перегруженный оператор

Способы использования объектов

Объект может быть создан в качестве:

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

void f(complex c){complex c1(2,3);}

Объекта в свободной памяти, создаваемого при помощи оператора new и уничтожаемого оператором delete.

complex *pc=new complex(2,3);

Нестатического члена-объекта, который создается и уничтожается тогда, когда создается и уничтожается содержащий его объект.

class X{

complex _c;

stack _s;

public:

X(complex &)

};

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

X::X(complex &c)

:_c(c),_s(100)//список инициализации

{}

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

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

complex cmas[10];

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

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

{static complex c=sin(3);}

Глобального объекта, объекта в пространстве имен или статического объекта класса, которые создаются один раз «во время запуска программы» и уничтожаются один раз, при ее завершении.

complex c(1,0);

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

k=plus(c1,complex(1,2)).imag(); return c1;

Объекта, размещенного в определенной области памяти.

Члена объединения union, который не может иметь ни конструктора, ни деструктора.

union economy{ rec c; double d};

Копирование объектов класса

Объекты можно копировать.

Существует 2 вида копирования объектов:

инициализация при помощи копирования

stack s2=s1;

присваивание

s2=s1;

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

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

class stack{

int cap;

int top;

char *s;//копирование по умолчанию создает

//стеки-сиамские близнецы

public:

stack(int capacity=10):cap(capacity),top(0),

s(new char[capacity]){}

~stack(){delete[] s;}

stack(const stack&);

stack& operator=(const stack&);//returns value for s1=s2=s3;

};

Использование:

stack a(100),c;a.push(`a');

stack b=a;с=a;

Для правильного копирования объектов данного класса копирующие функции нужно определить так:

#include <cstring>//for memcpy

stack::stack(const stack& st)

:cap(st.cap),top(st.top),s(new char[st.cap]){

//for(int i=0;i<cap;++i)s[i]=st.s[i];

memcpy(s,st.s,cap);

}

stack& stack::operator=(const stack&st){

if(this!=&st){

delete[] s;

s=new char[cap=st.cap];

//for(int i=0;i<cap;++i)s[i]=st.s[i];

memcpy(s,st.s,cap);

top=st.top;

}

return *this;

}

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

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

Для того, чтобы перемножить две переменные типа double и сложить с третьей типа double, мы можем написать:

x+y*z

Для переменных cx, cy, cz определенного нами ранее типа complex аналогичные действия мы выполняем так:

complex cx,cy,cz;

cy.mul(cz);

cx.add(cy);

Если же мы запишем

cx+cy*cz

то компилятор выдаст ошибку, т.к. встроенные операции «+» и «*» применимы только ко встроенным типам.

Однако C++ позволяет определить смысл многих операций (в том числе сложения и умножения) для определяемых пользователем типов, например:

class complex{

public:

...

complex operator+(complex);

complex operator*(complex);

private:

double re,im;

};

complex complex::operator +(complex a)

{

return complex(re+a.re,im+a.im);

}

complex complex::operator *(complex a)

{

return complex(re*a.re-im*a.im, re*a.im+im*a.re);

}

Если cy и cz имеют тип complex, то cy+cz означает cy.operator+(cz).

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

complex c1(r1,i1),c2(r2,i2);

c2=с2+c1*complex(2,3);

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

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

Операторные функции. Можно объявить функции, определяющие смысл следующих операторов:

+ - * / % = < > += -= *= ++ -- , -> [] () new delete

и других (всего 42).

Следующие операторы не могут быть определены пользователем:

:: (разрешение области видимости)

. (выбор члена класса или структуры)

.* (выбор через указатель на член класса)

?: (тернарный оператор)

Допускается переопределять существующие операторы, но нельзя создавать новые (например ** для степени).

Бинарные и унарные операторы.

Операторы - функциональные компоненты выражения. Аргументами операторов являются операнды. В зависимости от количества операндов операторы бывают бинарными (два операнда):

Например: a+b, a*=b, a<b, a[b], a&b, a&&b

и унарными (один операнд)

Например: -a, --a, &a, a++

Унарные операторы, в свою очередь, делятся на префиксные (-a, ++a) и постфиксные (a++).

Есть также один тернарный оператор, a?b:c , но переопределять его действие нельзя.

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

a*b+c>d (((a*b)+c)>d)

Порядок, в котором выполняются операторы, определяется их приоритетом. Например, приоритет оператора * выше, чем оператора +.

Кроме того, некоторые операторы изменяют (или могут изменять) свои операнды

++a, a++, a*=b, a=b

а некоторые - не изменяют

a+b, a-b, -a.

Правила опеределения операторных функций

Для любого бинарного оператора @ выражение aa@bb интерпретируется либо как

а) нестатическая функция-член с одним аргументом

aa.operator@(bb)

либо как

б) функция-не-член с двумя аргументами

operator@(aa,bb)

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

class X{

public:

void operator+(int);

X(int);

};

void operator+(X,X);

void operator+(X,double);

void f(X a)

{

a+1; //a.operator+(1)

1+a; //::operator+(X(1),a)

a+1.0;//::operator+(a,1.0)

}

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

Для любого префиксного оператора @ выражение @aa интерпретируется либо как

а) нестатическая функция-член без аргументов

aa.operator@()

либо как

б) функция-не-член с одним аргументом

operator@(aa)

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

Для любого постфиксного оператора @ выражение aa@ интерпретируется либо как

а) нестатическая функция-член с аргументом типа int

aa.operator@(int)

либо как

б) функция-не-член следующего вида:

operator@(aa,int).

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

class X{//члены

X* operator&();//префиксный унарный оператор & (чей-то адрес)

X operator&(X); //бинарный оператор & (И)

X operator++(int); //постфиксный инкремент

X operator&(X,X); //ошибка: 3 операнда

X operator/(); //ошибка: унарный оператор /

};

//функции-не-члены

X operator-(X); //префиксный унарный -

X operator-(X,X); //бинарный -

X operator--(X&,int); //постфиксный декремент

X operator-(); //ошибка: отсутствует операнд

X operator-(X,X,X); //ошибка: три операнда

X operator%(X); //ошибка: унарный оператор %

Тип комплексных чисел

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

Конструирование. Мы должны обеспечить создание объекта-комплексного числа следующими способами:

complex c1,//1

c2(1),

c3=2,

c4(3,4),

c5=complex(5,6);

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

class complex{

public:

complex():re(0),im(0){}//1.0

complex(double real):re(real),im(0){}

complex(double real,double imag):re(real),im(imag){}

private:

double re,im;

}

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

complex(double real=0,double imag=0):re(real),im(imag){}//1

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

c1+=c4;//2

c2+=7;

Для этого мы определяем оператор +=. У нас есть выбор: определить его в классе или как функцию-не-член класса. Так как нам нужен доступ к представлению класса (re, im), то объявляем в классе:

complex& operator+=(const complex& a);//2

Обратите внимание на объявление аргумента const complex& a. В этом случае формальный параметр a является ссылкой на фактический параметр. При этом в функцию передается адрес фактического параметра (4 байта). Альтернативой было бы сделать объявление следующим образом:

complex& operator+=(complex a);

В этом случае фактический параметр a инициализируется значением формального параметра (в данном случае 16 байт). По соображениям эффективности выбираем первый вариант. Возникает вопрос: почему бы для сокращения передаваемой в функцию информации использовать не ссылку, а указатель? То есть:

complex& operator+=(const complex *a);

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

c1+=&c4;

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

Для добавления действительного числа объявляем функцию-член класса

complex& operator+=(double a);//2

Определяем эти функции-операторы в .cpp - файле.

complex& complex::operator+=(const complex& a)//2

{

re+=a.re;//добавление действительной части аргумента к действительной части данного объекта

im+=a.im;//добавление мнимой части

return *this;//возврат значения

}

В данной функции выполняется два действия: 1) выполняется операция добавления и 2) выполняется возврат значения. Возврат значения нужен, чтобы результат оператора += можно было использовать в качестве операнда. Например:

с1=(с2+=с3)+7;

Результатом оператора += должно быть значение его левого операнда (в примере - c2) после того, как к нему добавлен правый операнд (в примере c3). Когда вызывается функция complex::operator+=, левый операнд для нее доступен как объект, для которого она вызвана. Таким образом, эта функция должна вернуть значение самого объекта. Это можно сделать, разименовав указатель this.

Почему же мы возвращаем ссылку, а не объект? Потому, что для типа int следующее выражение должно увеличить значение переменной a, а не копии переменной a:

(a+=b)++;// после того, как к a добавили b, увеличить a на 1.

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

Реализация второго оператора += несколько проще, так как добавление идет только к действительной части комплексного числа:

complex complex::operator+=(double a){

re+=a;

return *this;

}

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

c1=3+c5;//3

c1=c5+3;

c1=c4+c5;

Для этого определим оператор +. Опять у нас есть выбор: определить его как член класса или как не-член-класса. В пользу последнего решения есть два довода. Во-первых, мы не можем определить оператор-член-класса так, чтобы он был вызван для случая 3+c5, т.к. левый операнд не является объектом. Во-вторых, для выполнения сложения не требуется иметь доступ к представлению класса complex. Итак, определяем функции-не-члены класса:

complex operator+(const complex& a, const complex& b);//3.1

complex operator+(const complex& a, double b);//3.2

complex operator+(double a, const complex& b);

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

complex operator+(const complex& a,const complex& b){

complex r=a;

return r+=b;

}

complex operator+(const complex& a,double b){

complex r=a;

return r+=b;

}

complex operator+(double a,const complex& b){

complex r=b;

return r+=a;

}

И опять, как и в случае с +=, сложение complex с double проще, чем сложение двух complex. Сравнение. Мы должны иметь возможность сравнивать комплексные числа друг с другом и с double в виде, принятом для встроенных типов, например:

c2==c4;//4

c2==8;

3==c2;

Для этого определим оператор ==. Как и в случае с оператором +, мы не можем определить оператор == как функцию-член, т.к. она не может быть вызвана для случая 3==с2. Следовательно, это должна быть функция-не-член. Сравнение двух комплексных чисел:

bool operator==(const complex& a,const complex& b);//4.2

Модификаторы const используются для указания того, что функция не изменяет аргументы. Этой функции требуется доступ к представлению класса (re, im), однако представление класса имеет модификатор private: и к нему нельзя получить доступ из этой функции напрямую.

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

public:

double real()const{return re;}

double imag()const{return im;}

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

bool operator==(const complex& a,const complex& b){

return a.real()==b.real()&&a.imag()==b.imag();

}

Для выполнения сравнения complex с double и double с complex мы могли бы определить еще два оператора ==:

bool operator==(const complex& a,const double& b){

return a.real()==b && a.imag()==0;

}

bool operator==(const double& a,const complex& b){

return a==b.real() && 0==b.imag();

}

Следует заметить, что в этих функциях выполняются те же действия, что и в первом варианте оператора==, но вместо действительной части используется сам аргумент типа double, а вместо мнимой части - 0. То есть сравнение производится с комплексным числом, у которого вместо действительной части - аргумент типа double, а мнимая часть равна 0. Если бы мы могли из действительного числа получить комплексное указанным способом, мы могли бы работу двух последних операторов == возложить на первый оператор ==. Оказывается, такой механизм преобразования у нас уже имеется - это конструктор complex, в случае, когда он вызывается с одним аргументом типа double. Поэтому определять две последние функции operator== мы не будем. А для того, чтобы их работа была выполнена первым оператором ==, нам не нужно ничего определять дополнительно. Компилятор сам в нужных местах вызовет конструктор, чтобы из double получить complex. Например:

c2==c4;//operator==(c2,c4)

c2==8;//operator==(c2,complex(8))

3==c2;//operator==(complex(3),c2)

Ввод-вывод. И, наконец, мы хотим выводить комплексные числа принятым в C++ способом:

std::cout<<c1<<c2<<c3;

Для этого определим оператор вывода для комплексного числа:

std::ostream& operator<<(std::ostream& os, const complex& a)

{

return os<<'('<<a.real()<<','<<a.imag()<<')';

}

Теперь запись std::cout<<c1<<c2<<c3; эквивалентна следующему:

operator<<(operator<<(operator<<(std::cout,c1),c2),c3);

Класс Matrix. Перегрузку операторов вызова функции и индексирования рассмотрим на примере класса Matrix. Этот класс предоставляет динамически размещаемые двумерные массивы.

class Matrix{

public:

Matrix(size_t,size_t);

Matrix(const Matrix&);

~Matrix();

private:

size_t d1,d2;

int* m;

};

Определение класса содержит количество элементов d1 по первой и d2 по второй размерности массива. Тип size_t - это тип значения, возвращаемого оператором sizeof. В конструкторе выделяется память для хранения всех элементов данного массива, а базовый адрес выделенной памяти является инициализирующим значением для указателя m.

Matrix::Matrix(size_t dim1,size_t dim2)

:d1(dim1),d2(dim2),m(new int[size()]){}

Для хранения элементов двумерной матрицы могут быть использованы несколько вариантов организации памяти. В данном случае используется одномерный массив. Достоинства такой организации: простота удаления-освобождения памяти. Недостатки: для доступа к элементу используется умножение (см. ниже).

Деструктор выполняет delete[] для освобождения памяти, выделенной оператором new[] в конструкторе.

Matrix::~Matrix(){delete[] m;}

В конструкторе используется вспомогательная функция-член size(), возвращающая количество элементов массива. Так как эта функция используется только внутри класса, мы помещаем ее в раздел private:

private:

size_t size() const {return d1*d2;}

Создание матрицы размером 2x3 выглядит следующим образом:

Matrix m(2,3);

Мы хотим заполнять матрицу в цикле, например, так:

for(size_t i=0;i<m.dim1();++i)

for(size_t j=0;j<m.dim2();++j)

m(i,j)=rand();

Здесь функции-селекторы dim1() и dim2() возвращают размеры массива по соответствующим размерностям. Эти функции определены в пределах класса:

size_t dim1() const {return d1;}

size_t dim2() const {return d2;}

Обратите внимание на то, в какой форме записано обращение к элементу матрицы - m(i,j). К имени объекта справа приписаны скобки, что означает вызов функции с именем m с двумя параметрами (i,j). Оказывается, вызов функции - это тоже оператор C++. Чтобы запись m(i,j) действительно означала обращение к элементу матрицы, мы должны перегрузить этот оператор. Оператор (), а также операторы =, [] и -> можно перегружать только как нестатические функции-члены класса. Это ограничение гарантирует, что левый операнд указанных операторов будет объектом. Перегружаем оператор ():

int& Matrix::operator()(size_t dd1,size_t dd2){//1

assert(dd1<dim1());

assert(dd2<dim2());

return m[dd1*dim2()+dd2];

}

int Matrix::operator()(size_t dd1,size_t dd2)const{//2

assert(dd1<dim1());

assert(dd2<dim2());

return m[dd1*dim2()+dd2];

}

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

Тела обеих функций operator() одинаковы: выполняется проверка на допустимость индексирующих значений, после чего вычисляется позиция элемента в одномерном массиве m. Затем первая функция возвращает ссылку на данный элемент, а вторая - значение данного элемента. Именно возврат ссылки, а не значения позволяет изменять значение элемента массива, записывая m(i,j)=rand().

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

void print(const Matrix& m){

for(size_t i=0;i<m.dim1();++i){

for(size_t j=0;j<m.dim2();++j){

std::cout<<m(i,j)<<',';

}

std::cout<<std::endl;

}

}

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

int* Matrix::operator[](size_t dd1)const{

assert(dd1<dim1());

return &m[dd1*dim2()];

}

Вызов: m[3][4]

(m.operator[](3))[4]

Matrix& Matrix::operator=(const Matrix& t){

if(this!=&t){

delete[] m;

m=new int[(d1=t.dim1())*(d2=t.dim2())];

copy(t);

}

return *this;

}

Итераторы

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

Давайте посмотрим, как мы заполняли массив matrix на предыдущем занятии:

for(size_t i=0;i<m.dim1();++i)

for(size_t j=0;j<m.dim2();++j)

m(i,j)=rand();

Для этого мы организовали два вложенных цикла. При этом нам понадобилось использовать две индексные переменные, вызывать две функции, а также следить за тем, чтобы переменная, наращиваемая до dim1() (это i), была первым индексом в m(i,j), а переменная, наращиваемая до dim2() (здесь это j), была вторым индексом.

Однако перебор элементов можно организовать с помощью специального объекта - итератора. Имея в своем распоряжении итератор, мы можем сделать две вещи:

а) получить доступ к текущему элементу,

б) перейти к следующему элементу.

Изменим класс Matrix, добавив в него определение итератора и две функции:

typedef int* iterator;

iterator begin()const{return m;}

iterator end()const{return m+size();}

Функции begin() и end() возвращают значения, которые являются «границами» матрицы для итератора. Теперь заполнение массива мы можем организовать так:

for(Matrix::iterator p=m.begin();p!=m.end();++p)

*p=rand();

Здесь с помощью итератора p мы

а) получаем доступ к текущему элементу путем разименования указателя,

б) переходим к следующему элементу, производя инкремент указателя.

Следуя идиоме итерационного перебора, напишем функцию, печатающую матрицу:

void print_iter(const Matrix& m){

for(Matrix::iterator p=m.begin();p!=m.end();++p)

std::cout<<*p<<',';

}

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

Теперь сделаем итератор для перебора элементов матрицы в обратном направлении. По прежнему для перехода к следующему элементу (элементу с меньшим адресом) будем использовать оператор инкремента. Теперь нам не подходит указатель, так как при вызове ++p итератор должен идти в сторону уменьшения. Поэтому нам придется определить класс итератора:

Так как этот класс используется исключительно с классом Matrix, то определим этот класс внутри класса Matrix

class Matrix{

...

class reverse_iterator{

public:

reverse_iterator(int *p):_p(p){}

int& operator*(){return *_p;}

void operator++(){--_p;}

bool operator!=(reverse_iterator r)const{return _p!=r._p;}

...

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

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

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

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

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

  • Технологии программирования. Сущность объектно-ориентированного подхода к программированию. Назначение Си, исторические сведения. Алфавит, базовые типы и описание данных. Структуры и объединения. Операторы Си++. Функции. Библиотека времени выполнения.

    курс лекций [51,9 K], добавлен 03.10.2008

  • Создание программного обеспечения - системы имитационного моделирования на тему "Производственная линия с пунктами технического контроля". Описание входных и выходных данных. Объектно-ориентированное программирование. Диаграммы модулей и процессов.

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

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

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

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

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

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

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

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

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

  • Понятие алгоритма и его характеристики как основного элемента программирования. Формы представления алгоритмов, основные алгоритмические структуры. Структурное и событийно-ориентированное программирование. Объектно-ориентированное программирование.

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

  • Методология объектно-ориентированного программирования в Java. Понятия класса, объекта и объектной переменной. Динамическая и статическая объектные модели. Логическое структурирование приложения. Наследование в Java. Отличия интерфейсов от классов.

    курс лекций [547,2 K], добавлен 01.05.2014

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

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

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

    презентация [74,8 K], добавлен 14.10.2013

  • Понятие объектно-ориентированного программирования, общая характеристика языков высокого уровня. Разработка программного обеспечения для реализации компьютерной игры "пинбол" с помощью императивного программирования в среде Microsoft Visual Basic.

    курсовая работа [428,9 K], добавлен 19.09.2012

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

    курсовая работа [2,6 M], добавлен 17.11.2014

  • Основная цель технологии СОМ (объектная модель компонентов) - обеспечение возможности экспорта объектов. Объектно-ориентированное программирование и его место в программировании. Принципы и применение описаний информационных систем (UML и аналоги).

    курсовая работа [698,3 K], добавлен 09.12.2013

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

    презентация [65,2 K], добавлен 05.01.2014

  • Объектно-ориентированное программирование, операторы SQL. Создание базы данных поступабщих с помощью .NET технологии, WPF и MS SQL Server. Структура системы: хранение логинов и паролей, безопасность, системные требования. Руководство пользователя.

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

  • Особенности объектно-ориентированного проектирования. Основные понятия объектно-ориентированного подхода. Основы языка UML, варианты его использования. Диаграммы классов и взаимодействия. Разработка диаграммы прецедентов (вариантов использования).

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

  • Характеристика модульного программирования: процедуры и функции, модули и их структура, открытые массивы и строки, нетипизированные параметры. Способы передачи параметров в подпрограммы в Borland Pascal. Объектно-ориентированное программирование.

    контрольная работа [28,9 K], добавлен 28.04.2009

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

    презентация [912,2 K], добавлен 22.10.2013

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