русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Преимущества о-о подхода


Дата добавления: 2015-07-09; просмотров: 615; Нарушение авторских прав


Операторы. Перегрузка операторов. Правила перегрузки. Синтаксис. Пример.

Операции подобны встроенным функциям языка. Они применяются к выражениям-операндам. Операции бывают:

- Бинарные - один операнд помещается перед знаком операции, другой после (X+Y).

- Унарные - имеют один операнд, помещаемый после знака операции (-X).

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

Можно перегружать любые операции, существующие в С++, за исключением: точка (.), разыменование (*), разрешение области действия (::), условная (?:) и sizeof. Нельзя перегружать операции препроцессора.

Функции-операции, реализующие перегрузку операций, имеют вид

<тип> operator операция ([операнды]) {тело функции}

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

Для перегрузки операций существуют такие правила:

- Приоритет и правила ассоциации для перегруженных операций остаются теми же самыми, что и для операций над встроенными типами.

- Нельзя изменить поведение операции по отношению к встроенному типу.

- Функция-операция должна быть либо элементом класса, либо иметь один или несколько параметров типа класса.

- Функция-операция не может иметь аргументов по умолчанию.

- Функции-операции, кроме operator=(), наследуются.

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



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

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

1). class house {…

house &operator++() {++number; return *this;}}

house cotteg;

cout <<(++cotteg).get_number(); // унарная функция-операция определена внутри класса: операндом является вызвавший ее объект (без параметров)

2). class house {…

friend house &operator++(house&H);};

house &operator++(house&H) {++H.number; return H;} //функция-операция определена вне класса: должна иметь один параметр типа класса

3). Void change_number(int n) {number==n;}

house &operator++(house&H) {

int numb=H.get_number(); numb++; H.change_number(numb)} //в описание класса house вводится метод change_number, следовательно, операция перегружается с помощью обычной функции, описанной вне класса

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

1). class house {…

bool operator>(const house&H){

if (number>H.number)return true;

return false;}}; //внутри класса, след., нестатический метод с параметрами, при этом вызвавший ее объект считается первым операндом

2). bool operator>(const house&H1, const house&H2){

if (H1.get_number()>H2.get_number())return true;

return false;}}; //функция вне класса, след., имеет 2 параметра типа класса

При перегрузке операций полезно помнить следующее:

- C++ не умеет образовывать из простых операций более сложные. Например, в классе со сложением строк мы определили присваивание и сложение; но это не значит, что тем самым будет автоматически определено присвоение суммы (+=). Такую операцию нужно реализовывать отдельно.

- Невозможно изменить синтаксис перегруженных операций. Одноместные операции должны быть одноместными, а двухместные — двухместными.

- Нельзя изобретать новые обозначения операций.

Желательно сохранять смысл перегружаемой операции. Например, конкатенация — естественная семантика сложения для строк.

 

8. Оператор присваивания. Назначение. Синтаксис. Пример.

Оператор присваивания присваивает значение некоторой области памяти. Например, оператор S=38 присваивает целое число области памяти, выделенной для переменной S. Знак = обозначает операцию присваивания. Язык С++ поддерживает целое семейство операторов присваивания. Большинство из них, такие как += и -=, перед присваиванием значения выполняют еще какую-либо операцию (сложение и вычитание). Исключением является только оператор, выполняющий только присваивание (=). Другой уникальной чертой языка является то, что согласно синтаксису, присваивание является одним из вариантов выражения, и оно может быть частью другого выражения. Как и все выражения, выражение присваивания превращается в инструкцию при добавлении точки с запятой после него: х = у; Общим свойством всех операторов присваивания является то, что они изменяют содержимое левого операнда. Таким образом, этот операнд должен быть выражением, подобным простой переменной, и иметь для своего значения определенное место в памяти ПК. Для таких выражений вводится специальное название value.

8. Оператор присваивания. Назначение. Синтаксис. Пример.

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

Нельзя Fs=As. При побитной копии объекта: Fs.ps=As.ps.

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

Вот пример класса с операцией присваивания:

class Time { int hr, min;

public:

Time(int h, int m): hr(h), min (m) {}

Time &operator=(const Times); // Операция присваивания

};

Time &Time::operator=(const Time &src)

{if(&src == this) // Проверка на самоприсваивание

error("Self assignment!");

hr = src.hr; min = src.min;

}

return *this; // Возвращает ссылку на свой объект

int main() {

Time start (17,45); Time current (18, 0);

start = current; // Вызывает operator=

return 0;

}

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

Параметры операции присваивания могут иметь тип либо имя_класса&, либо const имя_класса&. Последнее предпочтительнее, так как простая ссылка на класс не позволяет копировать константные объекты.

9. Операторы new и delete. Назначение. Синтаксис Пример.

Операция new создает объект типа имя_типа, к которому он применен. Время жизни объекта, созданного с помощью new, не ограничено областью видимости, в которой он создан. Операция new возвращает указатель на созданный ей объект. Когда объект является массивом, возвращается указатель на его первый элемент. Синтаксис: имяТипа имяУказателя = имяТипа

Например, и new int и new int[10] возвращают int*. Операция new для получения памяти вызывает функцию

void* operator new (long);

Параметр задает требуемое число байтов. Память будет инициализирована. Если operator new() не может найти требуемое количество памяти, то она возвращает ноль. Операция delete уничтожает объект, созданный операцией new. Ее результат является void. Операнд delete должен быть указателем, возвращенным new. Результат применения delete к указателю, который не был получен с помощью операции new. Однако уничтожение с помощью delete указателя со значением ноль безвредно. Чтобы освободить указанную память, операция delete вызывает функцию

void operator delete (void*);

В форме delete [ выражение ] выражение

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

Операторы new и delete. Назначение. Синтаксис Пример.

Класс может определять свои собственные операции new и delete (new[] и delete [] для массива объектов):

- Функция имя_класса: : operator new () вызывается при создании динамических объектов.

- Функция имя_класса: : operator new [ ] () вызывается при создании динамических массивов объектов.

- Функция имя_класса:: operator delete() вызывается при удалении динамических объектов.

- Функция имя_класса:: operator delete [] () вызывается при удалении динамических массивов.

Операции new и delete – один из подходов к динамическому распределению памяти.

Если речь идет о динамическом размещении в памяти объектов библиотеки компонентов C++Builder, то лучше всего это делать с помощью операции new. Операция new имеет следующий синтаксис: <::> new <размещение> тип <(инициализатор)>

Операция возвращает указатель на динамически размещенный в памяти объект. Операция разрешения области действия (::) позволяет обратиться к глобальной версии new, если наряду с ней возможно использование перегруженных операций. Элемент размещение используется (если он предусмотрен перегруженной версией) для дополнительной информации о месте размещения в памяти. Инициализатор задает начальное значение создаваемого объекта. Таким образом, обязательно должен быть указан только тип данных. Например: double *A = new double;

В данном случае в памяти динамически создается объект – действительное число. В дальнейшем доступ к нему осуществляется как *A. Например: *A = 5.1;

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

double В = *new double;

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

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

<::> delete <выражение>

<::> delete [ ] <выражение>

delete <имя массива> [ ];

Например:

double *A = new double;… delete А;

или double *A = new double [100];… delete [ ] А;

Операция delete освобождает память, но сама не задает указателю на эту память значения NULL. Поэтому желательно это делать программно, чтобы случайно в дальнейшем не воспользоваться указателем, который уже ни на что не указывает: delete А; А = NULL;

 

10. Операторы ввода-вывода. Назначение. Синтаксис. Пример.

Объекты iostream:сerr – этот объект представляет последовательный доступ к стандартному устройству вывода ошибокcerr << “Ошибка с кодом”;cin – объект cin представляет последовательный доступ к стандартному устройству ввода информации.clog – объект clog представляет последовательный строковый доступ к монитору. Clog <<“Ошибка” cout – объект cout представляет последовательный строковый доступ к устройству стандартного вывода. Основные функции консольного ввода-вывода:getchar() возвращает очередной символ с клавиатуры как целое;gets(s) читает символы с клавиатуры до появления символа новой строки и помещает их в строку s. Возвращает указатель на s.printf(fmt, par1, par2, …) выводит строку параметров par1, par2, … в формате, определенном строкой fmt, на стандартное устройство вывода. Возвращает число выведенных символов.рutchar(ch) выводит символ ch на стандартное устройство вывода. puts(s) выводит строку s на стандартное устройство вывода, добавляя в конце символ новой строки.scanf(fmt, par1, par2, …) вводит строку параметров par1, par2, … в формате, определенном строкой fmt, со стандартного устройство ввода.

11. Оператор приведения типа. Назначение. Синтаксис. Пример.

Оператор приведения типа позволяет изменить тип выражения: он вычисляет значение выражения, изменяет его тип и присваивает значение нового типа результату. Синтаксис:(имяТипа) значение //преобразует значения в данные типа имяТипа (форма записи определена в С)имяТипа (значение) //преобразует значения в данные типа имяТипа (в С++)Выражение имеет то же значение, что и выражение expr, но оно приведено к типу, определенному в скобках: (type) expr. Хотя выражение (type) expr теоретически имеет то же значение, что и выражение expr, действие оператора приведения типа может привести к преобразованию данных, которые изменят внутреннее представление числа в памяти. Альтернативный синтаксис: type (expr).

Оператор приведения типа. Назначение. Синтаксис. Пример.

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

operator имя_нового_типа();

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

- У процедуры преобразования нет параметров.

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

- Процедура преобразования может быть объявлена виртуальной.

- Она может наследоваться.

Вот пример процедуры преобразования:

#include <stdio.h>

class Time { int hr, min;

public:

Time(int h, int m): hr(h), min(m) {} //Конструктор

operator int(); // Процедура преобразования

};

Time::operator int() {

// Преобразует время в число секунд от начала суток:

return (3600*hr + 60*min);}

main ()

{int h = 7;int m = 40;

Time t (h, m); // Последний параметр вызывает Time::operator int():

printf("Time: %02d:%02d = %5d seconds.\n", h, m, (int)t);

return 0;

12. Статические члены класса. Назначение. Синтаксис. Пример.

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

 

12. Статические члены класса. Назначение. Синтаксис. Пример.

Можно объявить элемент класса (данные или функцию) как статический.

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

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

class SomeClass

{static int iCount; // Объявление статического элемента

};

int SomeClass::iCount = 0; // Определение статического элемента.

Обращаться к открытым статическим элементам класса можно либо через любой его представитель операциями “.” и “->”, либо с помощью операции разрешения области действия (SomeClass : : iCount). Последний способ предпочтительнее, так как ясно показывает, что элемент не связан с конкретным объектом.

Статические элементы-функции. Функция класса, объявленная с модификатором static, не связывается ни с какими его конкретными представителями. Другими словами, ей не передается указатель this в качестве скрытого параметра. Это означает, что:

- Статическая функция-элемент может вызываться, даже если никаких

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

- Такая функция не может быть объявлена виртуальной.

13. Наследование классов. Ключи доступа. Одиночное наследование. Синтаксис. Пример.

Наследование - удобное средство С++, позволяющее избежать переписывания функций и данных из одного класса в другой. Наследование позволяет переходить от классов, общими свойствами, к все более и более специализированным. Когда один класс наследуется из другого, исходный класс называется базовым, а наследующий – производным. Достаточно определить класс производный от другого (базового) класса, объявить и определить в нем новые члены, и он, помимо новых, унаследует все члены базового класса. Использование производных классов позволяет строить целые иерархии классов. Синтаксис:Class NewClass: public BaseClass{ // компоненты класса}; Класс NewClass помимо своих данных и функций, автоматически содержит все данные и функции базового класса BaseClass. Можно изменять и дополнять их используя операторы доступа к членам объекта (.) или (–>). В данном контексте ключевое слово рublic является спецификатором доступа и определяет механизм доступа к членам базового класса со стороны производного. Одиночное наследование – это когда базовым является один класс:ж

Наследование классов. Ключи доступа. Одиночное наследование. Синтаксис. Пример.

 

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

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

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

Синтаксис производного класса следующий:

class имя класса: ключ доступа имя_базового класса [, ...] {тело_объявления_класса } ;

Ключ_доступа — это одно из ключевых слов private, protected или public.

Ключ доступа определяет “внешний” доступ к элементам базового класса через объекты производного. Что касается доступа самого производного класса к элементам базового класса, то ключ доступа на него не влияет. Для производного класса доступны разделы protected и public базового класса; раздел private строго недоступен вне области действия базового класса.

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

Например, при наследовании с ключом public права доступа к элементам базового класса остаются неизменными; при закрытом наследовании (ключ private) все элементы базового класса будут недоступны за пределами производного класса.

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

class First { public:

void FFunc(void) ;//... }

class Second: private First { public:

First::FFunc; // First::FFunc() открыта в классе Second.

}

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

Правила наследования различных методов:

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

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

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

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

2) Не наследуется операция присваивания, она должна быть явно определена в производном классе.

3) Деструкторы не наследуются, и если программист не описал в производном классе деструктор, он формируется по умолчанию и вызывает деструкторы всех базовых классов.

4) Поля, унаследованные из базового класса, недоступны функциям производного класса, если они определены в базовом классе как private.

5) Производный класс может не только дополнять, но и корректировать поведение базового класса.

14. Виртуальные функции и полиморфизм. Синтаксис. Пример.

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

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

Полиморфизм функций – возможность воспользоваться несколькими функциями, имеющими одно и то же имя.

 

14. Виртуальные функции и полиморфизм. Синтаксис. Пример.

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

В C++ полиморфное поведение объектов обеспечивается механизмом виртуальных функций-элементов. Допустим, программа должна в числе всего прочего выводить на экран различные геометрические фигуры. Она определяет класс “фигура”, в котором предусмотрен виртуальный метод “нарисовать” (в C++ это был бы абстрактный класс). От данного класса можно произвести несколько классов — “точка”, “линия”, “круг” и т. д., — каждый из которых будет по-своему определять этот метод.

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

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

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

class D;

virtual px();

class U: public class D;

px(); - заменяет одноименную функцию в классе D.

// Демонстрация виртуальной функции.

#pragma hdrstop

#include <condefs.h>

#include <stdio.h>

class One {

// Базовый класс

public:

virtual void ShowVirtO // Виртуальная функция.

{printf("It's One::ShowVirt()!\n");}

void ShowNonVirt() // Не-виртуальная функция.

{printf("It's One::ShowNonVirt()!\n") ;}

};

class Two: public One { // Производный класс

public:

virtual void ShowVirt()

{printf ("It's Two::ShowVirtO !\n") ;}

void ShowNonVirt ()

{printf("If s Two::ShowNonVirt ()!\n") ;}

};

int main(void)

{Two derived; One *pBase = sderived;

pBase->ShowVirt(); // Вызовет Two::ShowVirt()

pBase->ShowNonVirt(); // Вызовет One::ShowNonVirt()

// Следующий вызов подавляет виртуальный механизм:

// pBase->One::ShowVirt();

// Явно вызывает One::ShowVirt().

return 0;}

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

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

Виртуальная функция не может быть статической.

 

15. Абстрактные классы и чистые виртуальные функции. Синтаксис. Пример.

Признаком чисто виртуальной функции являются символы “= 0” в конце ее объявления. Это означает, что определение функции нет в базовом классе, и такое определение произойдет в производных классах. Абстрактный класс это класс, который не может иметь экземпляров. Абстрактный класс пишется в предположении, что его конкретные подклассы дополнят его структуру и поведение, скорее всего, реализовав абстрактные операции. Абстрактный класс не может быть использован напрямую для создания объектов, допускается объявление указателей на такие классы: Class *NewClass. Пример:class NewClass{ public: virtual void virt_func(void) =0;};

15. Абстрактные классы и чистые виртуальные функции. Синтаксис. Пример.

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

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

Абстрактные классы

1) не могут иметь представителей;

2) не могут использоваться в качестве типа параметров или возвращаемых значений;

3) не могут участвовать в явных приведениях типа.

Тем не менее, можно объявлять указатели или ссылки на абстрактный класс.

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

Виртуальная функция-элемент некоторого класса может быть объявлена чистой. Это выглядит так:

virtual тип имя функции(список параметров} = 0;

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

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

class Shape { // Абстрактный базовый класс

public:

virtual Shape () {} //На всякий случай...

virtual void Draw() =0; //Чисто виртуальная функция //...};

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

class Line: public Shape {//...

public:

void Draw() }

class Rectangle: public Shape { //...

public:

void Draw(){//...}} // И т.д.

 

 

16. Множественное наследование классов. Проблемы. Виртуальное наследование. Синтаксис. Пример.

Множественное наследование – описывает класс, который включает более одного непосредственного базового класса. Как и в случае с одиночным наследованием, общедоступное множественное наследование должно выражать отношение is-a (является).Класс D является производным от классов А, В и С:class D: public A, public B, public C{ // компоненты класса D}В результате такого определения класса он автоматически получает все члены, определенные в классах А, В и С. Однако множественное наследование может привести к возникновению ряда проблем. Например, если и в класс А и в классе В есть переменная х, то к неизвестно к какой из них выполняется обращение в классе D. В это случае можно воспользоваться оператором уточнения (::): А::х и В::х.

16. Множественное наследование классов. Проблемы. Виртуальное наследование. Синтаксис. Пример.

Язык C++ допускает не только простое, но и сложное наследование, т. е. наследование от двух и более непосредственных базовых классов. Это позволяет создавать классы, комбинирующие в себе свойства нескольких независимых классов-предков.

Для иллюстрации сложного наследования возьмем пример с “сообщениями таймера”. Понятия времени и понятие сообщения — независимые, и, возможно, в программе будут другие классы, родственные “времени” и “сообщению”. Поэтому вполне разумным будет определить для них отдельные классы и породить от них третий класс, применив методику сложного наследования:

#include <stdio.h> #include <string.h>

class Time {// Базовый класс - время

protected:

int hr, min;

public:

Time(int h=12, int m=0): hr(h), min (m):{}

void Show() ;};

void Time::Show() {

printf("%02d:%02d\n", hr, min);}

class Message { protected: // Базовый класс - сообщение

char *msg;

public:

Message(char*) ;

~Message () { delete[]msg; }

void Show () ;}

Message::Message(char*msg)// Конструктор Message

{msg = new char[strlen(str)+1];

strcpy(msg, str);}

void Message::Show()

{printf(%s\n", msg);}

// Производный класс сообщений таймера

class Alarm: public Time, public Message { public:

Alarm(char* str, int h, int m): Time(h, m), Message(str) {}

void Show ();};

Alarm::Show() // Переопределяет базовые Show()

{printf("%02d:%02d: %s\n", hr, min, msg);}

int main() {

Alarm a("Test Alarm!!!", 11, 30);

a.Show() ;

return 0;}

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

Неоднозначности при сложном наследовании

В иерархии классов со сложным наследованием вполне может получиться так, что класс косвенно унаследует несколько экземпляров некоторого базового класса. Если В и С оба являются наследниками A, a D наследует В и С, то D получает двойной набор элементов класса А. Это может приводить к неоднозначностям при обращении к ним, что будет вызывать ошибки времени компиляции. Вот иллюстрация:

class A { public:

int AData;

void AFunc (); ... };

class B: public A {// ... };

class C: public A {...};

class D: public B, public С // Двукратно наследует А

{...};

int main (void)

{D d;

d.AData = 0; // Ошибка! d.AFunc ();

return 0;}

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

d.B::AData= 0; d.С::AFunc();

Виртуальные базовые классы

В качестве альтернативы операции разрешения области действия при сложном наследовании можно потребовать, чтобы производный класс содержал только одну копию базового. Этого можно достигнуть, описав базовый класс при наследовании от него как виртуальный c помощью ключевого слова virtual. Пример, который делает класс А виртуальным базовым классом:

class A { public:

int AData;

void AFunc ();//...};

class B: public virtual A // A - виртуальный базовый класс {};

class C: public virtual A // A - виртуальный базовый класс {// ... };

class D: public B, public С // Содержит только одну копию А {// ... };

int main(void) {

D d;

d.AData = 0; // Теперь неоднозначности не возникает.

d.AFunc(); return 0;}

 

17. Параметризованные классы. Синтаксис. Пример.

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

 

17. Параметризованные классы. Синтаксис. Пример.

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

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

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

Шаблоны классов задаются аналогично шаблонам функций. Описание шаблона отличается от описания класса первой строкой: template <class идентификатор> class имя класса

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

template <class T> class Matrix

{…};

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

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

template<class T>

{double Absolut(T&a);

return abs(a);

}

double x;

Absolut(x);

 

18. Этапы развития индустрии программного обеспечения. Современный метод управления разработкой.

В ходе эволюционного развития теории проектирования ПО, сложились три основные модели ЖЦ, которые выражают последовательность этапов ЖЦ ПО.

Первой, по времени появления являлась каскадная модель (рис. с обр. стороны):

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

· модель состоит из последовательно расположенных этапов,

· каждый этап полностью заканчивается до того как начнется следующий,

· этапы не перекрываются во времени (следующий этап не начинается, пока не завершится предыдущий).

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

· результат появляется только в конце разработки.

Только на стадии тестирования происходит устранение и выявление ошибок.

Следующей явилась итерационная модель. Это модель разработки ПО с обратными связями м/у этапами. На каждом из этапов проводятся проверки и корректировки разрабатываемой ИС. Это позволяет существенно снизить трудоемкость отладки по сравнению с каскадной моделью. При нахождении ошибки производится повторная проверка

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

 

19. Объектно-ориентированный подход к разработке ПО. Объектно-ориентированные модели.

К о-о моделям относятся:

1.Статическая логическая модель (описывает классы системы).2.Логическая динамическая модель (модель объектов системы и их взаимодействие (диаграмма взаимодействия) ).3.Физическая статическая модель (Архитектура модулей программной системы; архитектура аппаратного обеспечения).4.Физическая динамическая модель (архитектура процессов системы).

Модели в о-о подходе строятся постепенно.

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

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

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

Преимущества о-о подхода

Сокращение числа возможных ошибок.

Повторное использование.



<== предыдущая лекция | следующая лекция ==>
Основные категории, принципы маркетинговой деятельности. | Недостатки о-о подхода


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.081 сек.