Главная Случайная страница


Полезное:

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


Категории:

АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника






Модульное проектирование программ





Наумов, В.Ю.

Информатика. Основы объектно-ориентированного программирования. Конспект лекций: учеб. пособие / В. Ю Наумов, Л. Г. Акулов, О. А. Авдеюк, Е. С. Павлова; ВолгГТУ. – Волгоград, 2013. – 249 с.

ISBN 978–5–9948–0269–4

 

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

 

Ил. 115. Табл. 10. Библиогр.: 8 назв.

 

ISBN 978–5–9948–0269–4 © Волгоградский государственный

технический университет, 2013.

© Л. Г. Акулов, Е. С. Павлова,

В. Ю. Наумов, О. А. Авдеюк, 2013

 

 


Содержание

1. Модульное проектирование программ.. 5

2. Основные возможности C++. 8

3. C++ как Си с классами. 17

4. Определение класса. 19

5. Сокрытие информации. 20

6. Описание член-функций. 22

7. Неявный указатель this 23

8. Конструкторы.. 24

9. Деструктор. 28

10. Пример программы с использованием классов. 30

11. Статические члены класса. 32

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

13. Наследование. 36

14. Наследование и доступность. 41

15. Композиция. 49

16. Динамическое связывание. 52

17. Виртуальные функции. 55

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

19. Перегрузка операций. 64

20. Перегрузка операции присваивания. 68

21. Программа для работы с комплексными числами. 72

22. Перегрузка операций и преобразование типов. 74

23. Индексирование. 75

24. Функция operator() 77

25. Операция выбора элемента. 79

26. Перегрузка операций new и delete. 80

27. Ввод и вывод. 83

28. Ввод и вывод встроенных типов. 84

29. Состояние потока. 87

30. Ввод и вывод пользовательских типов. 91

31. Форматированный ввод. 92

32. Шаблоны функций. 98

33. Классы и шаблоны.. 106


Модульное проектирование программ

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

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

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

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

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

Программы разбивают на модули, чтобы:

1. упростить их разработку и реализацию;

2. облегчить чтение программ;

3. упростить их настройку и модификацию;

4. облегчить работу с данными, имеющими сложную структуру;

5. избежать чрезмерной детализации алгоритмов;

6. обеспечить более выгодное размещение программы в памяти ЭВМ.

Свойства модуля:

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

2. Модуль должен реализовывать единственную функцию.

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

4. Модуль имеет один вход и один выход.

5. Хороший модуль не использует глобальные переменные для общения с другим модулем, так как потом трудно отыскать, какой из них портит данные.

Подчиненность модулей удобно изображать схемой иерархии.

Рис.1 Схема иерархии.

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

Классификация строения программ.

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

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

2. Монолитно-модульное. Имеется достаточно большая монолитная главная часть программы, в которой производятся основные вычисления, и из которой происходят последовательные обращения к модулям.

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

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

5. Иерархически-хаотическое. Иерархическая (или последовательная) подчиненность модулей нарушена дополнительными связями.

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

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

Помимо модульности другим свойством, которое содействует предупреждению появления в программе ошибок, является структурированность.

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

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

Структурное программирование:

- ускоряет написание;

- облегчает отладку;

- разделяет работу между исполнителями;

- открывает возможности для дальнейших переделок программы.

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

2. Основные возможности C++

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

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

1. Комментарии. Введен новый символ комментария //. Вся строка после него считается комментарием.

2. Константы. В C++ для определения символических констант вместо директивы #define рекомендуется использовать объявление переменной с начальным значением и ключевым словом const.

Пример.

const int c=100;

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

double massiv[c];

3. Встраиваемые функции. В C++ для определения функции, которая должна встраиваться в текст программы, используется ключевое слово inline.

Пример.

inline double SUMMA (double a, double b)

{return (a+b);}

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

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

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

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

4. Объявление структур, смесей и перечислений. В C++, в отличие от Си, имена типов структур, перечислений и смесей рассматриваются как полноценные типы, определенные пользователем.

Пример.

Фрагмент Си-программы:

enum day {sun, mon, tue, wed,...};

struct path

{char str[40];

enum day week;

};

struct path list;

Тот же фрагмент на C++:

enum day {sun, mon, tue, wed,...};

struct path

{char str[40];

day week;

};

path list;

5. Объявление переменных. В C++ разрешено объявлять переменную в любом месте программы, где это необходимо. На объявление переменной накладывается лишь одно требование: переменная должна быть объявлена к моменту ее использования.

Пример.

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

s[i]=’\0’;

Переменная видна только в том блоке, в котором объявлена.

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

1):: - операция разрешения области видимости, которая позволяет обратиться к глобальной переменной, если видима локальная переменная с тем же именем.

Пример.

int i=0;

void main()

{char *i=”Hello”;

printf (“i-строка=%s \t i-целое=%d \n”, i,::i);

}

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

i - строка = Hello i - целое =2

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

2) В C++ для работы с динамической памятью введены две новые операции:

- операция new для распределения памяти;

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

Пример.

// Распределение одного элемента типа int

int *ip=new int;

// Распределение массива из 20 элементов типа str

struct str

{char name[40];

int m;

};

str *pstr=new str[20];

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

Операция delete имеет формат:

delete указатель; // для одиночных переменных

delete []указатель; //для массивов

С помощью операции delete может быть освобождена только память, ранее распределенная операцией new.

Пример.

int *first = new int[5];

int mas[5];

delete []first;

//Освобождение памяти из 5 элементов типа int

delete mas;

// Ошибка: mas не распределялась операцией new

7. Ссылки. В C++ введен новый тип данных – ссылка, который позволяет определить альтернативное имя переменной.

Синтаксис объявления ссылки:

тип & идентификатор2 = идентификатор1;

Такое объявление фактически назначает переменной с именем «идентификатор1» второе имя «идентификатор2». Ссылка при объявлении всегда должна быть проинициализирована, и затем ее уже нельзя изменить.

Пример.

int a,b;

int &alt=a; // alt является ссылкой на а

alt=b; // a=b

аlt++; // а++

Объявление ссылки напоминает объявление указателя, только вместо «*» используется «&». Пусть

int *point=&a;

тогда следующие условные выражения будут всегда истинны:

*point == alt и point == &alt.

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

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

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

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

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

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

Пример.

На языке Си:

void swap (int *a, int *b)

{int temp=*a;

*a=*b;

*b=temp;

}

void main()

{int x=5, y=17;

swap(&x, &y);

}

Нa языке С++:

void swap(int &a, int &b)

{int temp=a;

a=b;

b=temp;

}

void main()

{void swap(int &, int &)

int x=5, y=17;

swap(x,y);

}

Недостатки использования ссылок:

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

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

Пример.

void swap(int &, int &);

main()

{int m1=5;

unsigned int m2=200;

swap(m1, m2);

}

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

int t1=(int) m2;

int &b=t1;

в результате поменяются местами переменные ml и tl, после выхода из функции переменная m2 останется неизменной.

В C++ функции могут не только принимать, но и возвращать ссылку на переменную.

Пример.

char &blank(char *);

char &blank(char *s);

{for(int i=0;(s[i]!=‘‘) && (s[i]!=’\0’); i++)

return s[i];

}

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

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

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

Значения по умолчанию присваиваются в заголовке функции следующим образом:

Пример.

void funct(double m, char='*', int i=2);

Для функции funct допустимы следующие вызовы:

funct(2.5, '\', 10); //m=2.5, ch='\', i=10

funct(2.5, '\'); //m=2.5, ch='\', i=2

funct(2.5); //m=2.5, ch='*', i=2

funct(2.5, 2); //Ошибка.

funct(2.5,'*', 2); //Правильно.

Следует придерживаться правил:

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

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

Пример.

Вариант 1 в объявлении:

int f(char, double=3.14);

void main()

{f('*');

}

int f(char c, double pi)

{...};

Вариант 2 в определении:

int f(сhar, double);

int f(char c, double pi=3.14)

{...}

void main()

{f('*');

}

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

Пример.

void swap(int&, int &);

void swap(double&, double&);

void swap(char& char&);

void main()

{int x1=10, y1=20;

double x2=10.1, y2=20.1;

char x3='x', у3 ='у';

swap(x1, у1);

swap(x2, у2);

swap(x3, у3);

}

void swap(int &a, int &b)

{int temp=a;

a=b;

b=temp;

}

void swap(double &a, double &b)

{...}

void swap(char &, char&)

{...}

12. Ввод-вывод. В C++, также как и в Си, нет встроенных средств ввода-вывода. Так как функции языка Си были недостаточно удобными, в C++ была разработана новая библиотека ввода-вывода (библиотека iostream), написанная на C++ и использующая концепции объектно-ориентированного программирования. В библиотечном файле iostream.h определены стандартные потоки ввода данных c клавиатуры cin и вывода данных на экран дисплея cout, а также соответствующие операции:

<< - операция записи данных в поток;

>> - операция чтения данных из потока.

Пример.

#include <iostream.h>;

...

cout<<"\n Введите количество элементов: ";

сin>>n;

 

3. C++ как Си с классами

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

Тип есть конкретное представление некоторой концепции (понятия). Например, имеющийся в C++ тип float с его операциями +, -, * и т.д. обеспечивает ограниченную, но конкретную версию математического понятия действительного числа. Новый тип создается для того, чтобы дать специальное и конкретное определение понятия, которому ничто прямо и очевидно среди встроенных типов не отвечает.

Пример.

В программе, которая работает с комплексными числами, можно было бы создать тип complex, для которого определить операции +, -, * и т.д.

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

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

Пример.

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

struct date {int month, day, year;};

// дата: месяц, день, год}

date today;

void set_date(date*, int, int, int);

vоid next_date(date*);

void print_date(date*);

...

set_date(&today, 1, 5, 2007);

next_date(&today);

print_date(&today);

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

Пример.

struct date

{int month, day, year;

void set(int, int, int);

void get(int*, int*, int*);

void next();

void print();

};

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

Пример.

date today; // сегодня

date my_burthday; // мой день рождения

void f()

{my_burthday.set(30, 12, 1950);

today.set(18, 1, 1985);

my_burthdaу.print();

today.next();

};

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

Пример.

vold date::next() {...}

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

Определение класса

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

Определение класса включает две части:

1. заголовок класса (class <имя>);

2. тело класса.

Тело класса содержит определение членов класса.

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

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

Член-функции отличаются от обычных функций следующим:

1. член-функции имеют полный привилегированный доступ ко всем членам класса;

2. член-функции имеют область видимости класса;

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

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

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

сlass date

{int month, day, year;

public:

vоid set(int, int, int);

void get(int*, int*, int*);

void next();

void print();

};

Сокрытие информации

Ключевые слова private и public используются при описании тела класса и управляют доступом к членам класса.

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

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

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

Пример.

void date:: print()

{cout<<day<<"."<<month<<"."<<year;

}

Однако функции не члены отгорожены от использования закрытых членов класса date.

Пример.

void backdate()

{today.day--;

} // ошибка

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

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

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

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

Если при объявлении класса ключевые слова public и private опущены, то по умолчанию подразумевается доступ private. Напомним, что в структурах ситуация обратная: по умолчанию подразумевается доступ public.

 

Date: 2016-05-13; view: 1568; Нарушение авторских прав; Помощь в написании работы --> СЮДА...



mydocx.ru - 2015-2024 year. (0.006 sec.) Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав - Пожаловаться на публикацию