logo
Шпора по информатике / 002

13.3 Оператор динамического выделения памяти new

Оператор new выделяет память при выполнении программы, а не при её

компиляции. Рассмотрим конкретный оператор программы

char *p6= new char[4]; (13.2)

Здесь p6 – указатель на символьный массив, причем тип массива записывается

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

количество элементов массива, что соответствует обычным объявлениям

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

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

Есть и другие формы записи оператора new для простых переменных

и многомерных массивов.

Если необходимо, то выделенную для массива память можно освободить

оператором delete.

** cpp1 ** 24.05.2004

Лекция 14

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

14.1 Объекты и процедуры при программировании

Вспомним, что ранее рассматривали следующие группы данных,

т.е. структуры: массивы, строки, структуры struct.

Объектно-ориентированное программирование (ООП) - это способ

(технология, метод) программирования с использованием сложных новых

структур - объектов.

Начало распространения ООП - 1985 г. Объекты в программе соответствуют

физическим объектам задачи и могут возникать, изменяться, взаимодействовать

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

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

"живут" и действуют. Работа с объектами возможна в Си++, в Паскале

с объектами, VBasic и некоторых других языках, но объектов нет в Cи.

Одно из важных применений ООП - это программирование сложных и

красивых интерфейсов. При ООП физические объекты не "крошатся" на мелкие

неузнаваемые части, а существуют в виде своих различных модификаций. При

этом сложные объекты создаются из более простых. Определение ООП

сформулируем следующим образом.

ООП - это метод программирования, при котором программа

рассматривается как совокупность объектов, которые в процессе

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

с другими объектами, меняют свое состояние и разрушаются.

Для понимания ООП можно считать, что большие программы разбиваются на

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

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

объединённые в некоторую структуру, называемую классом. Другими словами,

ООП имеет дело не с отдельными подпрограммами, а с их классами. Класс

характеризует объект, это как бы тип объекта, но очень сложный. Можно

говорить об объекте определенного класса. Есть аналогия с классами в

обычном понимании.

class A

class B

f (x,y)

main( )

Рис. 14.1 - Программа с классами

Класс - это абстрактный тип данных. Для объявления класса используется

слово class. Каждую простую переменную характеризует тип (например, int,

float, char в Си++), а объект характеризует класс т.е. имеем пары:

переменная - тип, объект - класс.

Слово class автор С++ взял из языка Simula-67.

Отметим, что термин " абстрактный тип данных" означает, что это

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

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

вызываемых в определенном порядке. Каждая подпрограмма решает какую-то

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

Поэтому программист должен прежде всего думать о разбиении большой задачи на

ряд подзадач, из решений которых компонуется общее решение. Такое

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

Рассмотрим примеры применения обоих методов программирования:

процедурного и ООП. Пусть нужна программа, которая перемещает

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

1) составить две независимых программы для каждой фигуры;

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

прямоугольника и овала;

3) составить программу для работы с объектами "прямоугольник" и "овал".

Сравним второй и третий подходы, а к первому подходу вернемся в конце

этого раздела.

При процедурном подходе программа на Си++ будет состоять из большого

количества функций, например, f1 - определение координат центра эллипса или

прямоугольника; f2 - определение размеров прямоугольника; f3 - определение

осей эллипса; f4 - определение координат четырех опорных точек для

рисования прямоугольника; f5 - опредление массива опорных точек овала для

соединения их прямыми; f6 - рисование прямой по двум заданным точкам и т.д.

Эти подпрограммы-функции условно показаны на рис.14.2 в виде "кирпичиков"

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

связи, показанные в качестве примера линиями.

f1

f1

Фигура

f2

Овал

Прямоугольник

f3

Рис. 14.2 Функции и объекты при процедурном и

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

Для ООП на Си++ в этой задаче можно определить 3 объекта: фигура,

прямоугольник, овал, см. рис.14.2. Все преобразования при перемещении

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

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

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

прямоугольник и овал делают все, что и фигура, но у каждого есть некоторая

специфика. Другими словами, соответствующими ООП, объекты "прямоугольник"

и "овал" наследуют все свойства объекта "фигура", но к ним добавляют свои

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

будут находиться внутри объектов и поэтому их проще осуществлять и

понимать.

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

Перейдем к взаимодействию программ и объектов. При процедурном

программировании подпрограммы объединяются в программу. Например, пусть

написаны на С++ две больших программы для овала и прямоугольника, о которых

говорилось выше. Каждая содержит основную подпрограмму main и десяток или

более подпрограмм. Первую программу имеет назовем Oval, а вторую - Priam.

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

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

программы, например, из Oval, к какой-то функции программы Priam невозможно.

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

объединяет данные и функции, т.е. является как бы самостоятельной

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

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

14.2 Объекты и их описание

Основная идея ООП состоит в том, чтобы связать данные с обрабатывающими

их функциями в единое целое - объект. Эту идею в ООП называют

инкапсуляцией. Capsula в латыни - это коробочка, ларчик; капсулы известны нам

также в технике и быту. Инкапсуляция - это "заключение в капсулу". т.е.

программирование с объектами - "коробочками". Покажем, как в С++ программе

формируются объекты.

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

аргументами. Эти же функции используются и для ООП на Си++, но они

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

важность ООП, функции, относящиеся к классу и объекту, называют методами.

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

Методы, объявляемые в классе, должны по возможности обращаться только

друг к другу и к данным этого же класса, хотя разрешено обращение и к

другим классам.

Если есть класс, то можно создавать любое количество объектов этого класса.

Каждый объект имеет тип, т.е. класс, который должен быть записан

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

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

Например, пусть A,B - это записи со структурой mystr, поля которой имеют

имена m, x, y. Ниже приводим фрагменты программы.

struct mystr (14.1)

{ int m; float x; float y; } ;

float ff ( int z) {

mystr A, B; . . . // создание двух структур

}

class Newcl { (14.2)

int m; float x, y; // это данные (поля объекта)

float f (int ); // это прототипы функций,

char g (int, float); // т.е. методов

} ;

. . . . . . . . // далее тексты функций

Следует обратить внимание на то, что описание объекта не создает объекта,

а только подготавливает условия для этого. Для создания объекта используется специальный оператор, что аналогично применению оператора mystr A,B; во фрагменте (14.1): struct описывет запись, а оператор mystr A,B; выделяет

память под конкретные переменные A, B, являющиеся записями.

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

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

и имя объекта можно рассматривать как аналог описания типа и имени

простой переменной. Рассмотрим конкретный пример объявления объектов.

. . . . . . . . . . . // строка точек соответствует описаниям класса (14.2) и других

main () { (14.3)

int d1, d2; float w; ........ // это описания данных

. . . . . . . . . . .

Newcl ob1, ob2; // это создание двух объектов класса (14.2)

w = ob2.f(d1); // вызов метода объекта

}

В заключение перечислим основные особенности объектов.

Каждый объект имеет

имя

поля

методы

- это характеристики объекта,

- это поведение объекта.

Объекты создаются и им посылаются сообщения, т.е. они

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

и их интерфейса, т.е. взаимодействия.

14.3. Три основных принципа ООП

Эти принципы, которые рассматриваются во всех книгах по ООП, имеют

следующие названия:

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

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

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

И н к а п с у л я ц и ю, т.е. объединение данных с функциями для их

обработки, рассмотрели в начале раздела 14.2.

Н а с л е д о в а н и е - это использование классов-родителей для создания

классов-потомков. Эти классы называют также базовым и производным.

Создав какой-либо класс, можно создавать его модифицированные версии,

которые наследуют переменные и методы класса - предка.

Объект-потомок автоматически наследует все данные и методы от родителя. Но

наследуемые свойства можно изменять и дополнять. Этот принцип позволяет

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

описания, и, кроме того, поведение объектов становится более ясным для

понимания. Следовательно, в ООП придерживаются правила "наследуй и

изменяй!"

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

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

для вещественного (x) и комплексного (z = x + iy) аргументов:

exp(z) = exp(x) ∙ ( cos(y) + i sin(y) ), т.к. вызов функции определяется не только

её именем, но и типом её аргументов. Компилятор для exp(x) и exp(z) вызывает

разные функции, но от пользователя это скрыто.

С полиморфизмом тесно связан раздел ООП – перегрузка функций.

Слово полиморфизм можно трактовать как многоформенность, многообразие.

Кратко поясним принцип полиморфизма на примере наследования свойств

нескольких классов-родителей. Пусть имеем базовые классы Ma1 и Pa1 и

производный класс R2, см. рис. 14.3.

Ma1 |█

Pa1 |█

||

R2

Рис. 14.3 - Класс R2 наследует свойства классов Ma1, Pa1

В каждом классе Pa1 и Ma1 есть функции fmet(x), причем эти методы вычисляют

абсолютно разные значения fmet, a совпадение имен является случайным.

Методы fmet условно показаны темнымы прямоугольниками на рис. 14.3. Пусть

создан объект obmy класса R2

R2 obmy ;

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

obmy.fmet(z);

Возникает вопрос, какой из двух методов - fmet из Ma1 или fmet из Pa1 -

будет выполняться. Для правильного вызова нужно написать

obmy . Мa1 :: fmet (z)

и объяснение рассматрим позже.

14.4 Достоинства и недостатки ООП

Д о с т о и н с т в а

1. Составление и понимание больших программ, т.е. содержащих

примерно 1000 операторов или более, становится менее трудным, т.е. они

пишутся быстрее и качественнее. Но следует отметить, что при этом стоимость

ООП продукта возрастает примерно на 20% по сравнению с традиционными

программами.

2. Возможно создание библиотеки классов, т.е. библиотеки объектов, а не

разрозненных подпрограмм или программ.

3. Упрощается обмен объектами при переносе их в другие программы.

4. Возможность динамического изменения как данных, так и методов.

Динамическое изменение - это изменение в процессе выполнения программы.

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

подпрограммы в процессе выполнения нельзя.

Н е д о с т а т к и

1. Для ООП нужно хорошее знание сложного языка, например, Си++.

2. Усложнение программирования.

14.5. Пример простой программы

Рассмотрим простую задачу : вывести N строк таблицы значений sin(x)

при 0 ≤ x ≤ π . Составим программу для решения по двум технологиям:

1) без применения ООП; 2) с применением ООП.

Способ 1 –“безклассовый”, т.е. процедурное программирование.

#include <iostream.h>

#include <math.h>

#include <conio.h>