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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



С.В.Сизов

 

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

методические указания

к выполнению лабораторных работ

для студентов специальностей

 

(очная форма обучения)

 

 

Одобрено редакционно-издательским советом института

 

 

Старый Оскол

УДК 681.5

ББК 32.965

 

Рецензент:

 

Сизов С.В. Объектно-ориентированное программирование. Методические указания к выполнению лабораторных работ. Старый Оскол, СТИ МИСиС, 2007. – 32 с.

 

 

 

Методические указания к выполнению лабораторных работ по курсу "Объектно-ориентированное программирование" для студентов специальностей: «230105 – Программное обеспечение вычислительной техники и автоматизированных систем», очной формы обучения.

 

Ó Сизов С.В.

Ó СТИ МИСиС


Содержание

Предисловие ……………………………………………………………………………………………….  
Лабораторная работа 1. Редакторы и компиляторы языков программирования С и С++ на различных платформах ……………………………………………………………………………………  
Лабораторная работа 2. Реализация простейшего класса ………………………………………………  
Лабораторная работа 3. Классы потоков ввода-вывода данных ……………………………………….  
Лабораторная работа 4-5. Статически и динамически подключаемые библиотеки (4 часа) ………...  
Лабораторная работа 6. Наследование и полиморфизм (4 часа) ……………………………………….  
Лабораторная работа 7. Классы библиотеки Qt: элементы виджета …………………………………..  
Лабораторная работа 8. Классы библиотеки Qt: элементы отображения ……………………………..  
Лабораторная работа 9. Методы и средства проектирования интерфейса пользователя в среде Windows ……………………………………………………………………………………………………  
Лабораторная работа 10. Методы и средства проектирования интерфейса пользователя в среде Unix …………………………………………………………………………………………………………  
Лабораторная работа 11. Создание интерфейса простых приложений ………………………………..  
Лабораторная работа 12. Классы библиотеки Qt: обработка событий ………. ………………………  
Лабораторная работа 13. Создание приложений баз данных (4 часа) …………………………………  

 

Предисловие

 

Дисциплина «Объектно-ориентированное программирование» являются основными в цикле специальных дисциплин ГОС ВПО специальности 230105.

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

Предлагаемые ниже лабораторные работы рассчитаны на 42 учебных часа: 20 часов по дисциплине «Программирование и основы алгоритмизации» и 22 часа по дисциплине «Объектно-ориентированное программирование». Они предполагают поэтапное освоение умений и навыков программирования на языке С++ от написания простых программ до создания объектно-ориентированных приложений. Характерной особенностью лабораторных работ является минимальное использование средств автоматического создания кода программ. В большинстве лабораторных работ достаточен простой текстовый редактор. Для создания интерфейса пользователя используется библиотека компонентов Qt, а так же средство быстрой разработки приложений Qt designer распространяющиеся по лицензии GNU.

Лабораторная работа 1.

 

Редакторы и компиляторы языков программирования С и С++

На различных платформах.

 

Цель работы: отработать умения и навыки работы с базовыми операциями компиляции и компоновки программ написанных на языках программирования С или С++:

 

Теория:

Редакторы текста программы.

Редактор – это программа, используемая для создания и модификации исходных текстов. В Unix системам доступно множество редакторов, но, очевидно, наиболее популярными и многофункциональными среди них являются KWrite, GNU Emacs, Kate, KEdit.

KWrite – не просто текстовый редактор для среды KDE с открытым исходным кодом. Это очень удобный редактор для программистов, и он может считаться, по крайней мере, частичной заменой более мощным приложениям. Его лучше всего использовать вместе с Konqueror для просмотра файлов с исходными кодами на разных языках. KWrite хорошо выполняет и обычные функции редактора текстов. Одна из его основных особенностей – подсветка синтаксиса, которая настраивается для разных языков программирования: C/C++, Java, Python, Perl, Bash, Modula 2, HTML и Ada. Работа с KWrite очень проста. Если вы хотя бы раз пользовались текстовым редактором, у вас не должно возникнуть проблем.

Для создания нового проекта в домашней директории создайте папку, например, Test. Для этого вызовите контекстное меню правой кнопкой мыши, далее «Создать – Папку» и укажите имя папки. Для создания файла в контекстном меню мыши выберите «Создать – Текстовый файл», и введите имя файла, например, main.cpp. По умолчанию, по клику левой кнопки мыши на этом файле, он открыться должен именно в KWrite. Если это не так, в контекстном меню мыши выберите опцию «Открыть с помощью...». В появившемся окне в списке «Известные приложения» откройте древообразный список «Редакторы», а в нем KWrite. Отметьте флажок «Запомнить связь с приложением для этого файла».

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

Открытие исходного файла C/C++

Чтобы запустить редактор Emacs, наберите emacs в окне терминала и нажмите <Enter>. Появится окно редактора, в верхней части которого имеется строка меню. Перейдите в меню Files, выберите команду Open Files и наберите имя требуемого файла в строке "мини-буфера" в нижней части экрана. При создании исходного файла на языке C используйте расширения.c или.h. В случае C++ придерживайтесь расширений.cpp,.hpp,.C или.H. Когда файл будет открыт, введите нужный текст и сохраните результат, выбрав команду Save Buffer в меню Files. Чтобы выйти из редактора, воспользуйтесь командой Exit Emacs в меню Files.

Те, кто испытывают раздражение от необходимости постоянно щелкать мышью, могут воспользоваться клавиатурными сокращениями, ускоряющими открытие и сохранение файлов, а также выход из редактора. Операции открытия файла соответствует сокращение C-x C-f. (Запись C-x означает нажатие клавиши <Control> с последующим нажатием клавиши <x>.) Чтобы сохранить файл, введите C-x C-s, а чтобы выйти из Emacs - C-x C-c. Лучше узнать о возможностях редактора можно с помощью встроенного учебника, доступного через команду Emacs Tutorial в меню Help. В нем приведено множество советов, которые помогут пользователям научиться эффективнее работать с Emacs.

Автоматическое форматирование

Программисты, привыкшие работать в интегрированной среде разработки, оценят имеющиеся в Emacs средства автоматического форматирования кода. При открытии исходного файла, написанного на C/C++, редактор самостоятельно определяет наличие в нем программного кода, а не просто текста. Если нажать клавишу <Tab> в пустой строке, редактор переместит курсор в нужную позицию, определяемую положением предыдущей строки. Особенностью редактора является возможность программировать практически любые операции, связанные с автоматическим форматированием. Благодаря этому были реализованы режимы редактирования множества видов документов, разработаны игры и даже СУБД (Введите команду M-x dunnet, если хотите поиграть в старомодную игру).

Синтаксические выделения

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

Чтобы включить режим цветовых выделений, откройте файл ~/.emacs и вставьте в него такую строку:

(global-font-lock-mode t)

Сохраните файл, выйдите из Emacs и перезапустите редактор. Теперь можете открыть нужный исходный файл и наслаждаться!

Вы обратили внимание на то, что строка, вставленная в файл.emacs, выглядит написанной на языке LISP. Это и есть LISP! Большая часть редактора Emacs реализована именно на этом языке. На нем же можно писать расширения к редактору.

Компиляторы GCC

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

Компиляторы, доступные в Unix-системах, являются частью коллекции GNU-компиляторов, известной как GCC (GNU Compiler Collection). В нее входят компиляторы языков C, C++, Java, Objective-C, Fortran и Chill.

В типичной, основанной на UNIX, системе команда компиляции и компоновки программы на C++ обозначается как СС. Чтобы скомпилировать и скомпоновать программу по имени welcom.c, наберите по приглашению UNIX

%СС welcom.c

В этом случае процесс компиляции и компоновки приведет к созданию выполняемого файла с именем: a.out.

Предположим, имеется проект, в который входят два исходных файла: один написан на C (main.c; листинг 1), а другой – на C++ (reciprocal.cpp; листинг 2).

Листинг 1. (main.c) Исходный файл на языке C

#include <stdio.h>

#include "reciprocal.hpp"

int main(int argc, char **argv)

{

int i;

i = atoi(argv[1]);

printf("The reciprocal of %d is %g\n", i, reciprocal (i));

return 0;

}

Листинг 2. (reciprocal.cpp) исходный файл на языке С++

#include <cassert>

#include "reciprocal.hpp"

double reciprocal(int i) {

// Аргумент не должен быть равен нулю

assert(i!= 0);

return 1.0/i;

}

Есть также файл заголовков, который называется reciprocal.hpp (листинг 3).

Листинг 3. (reciprocal.hpp) Файл заголовков

#ifdef __cplusplus

extern "C" {

#endif

 

extern double reciprocal(int i);

 

#ifdef __cplusplus

}

#endif

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

Компоновка объектных файлов

После того как файлы main.c и reciprocal.cpp скомпилированы, необходимо их скомпоновать. Программы написанные на С/С++ обычно содержат ссылки на функции, определенные где-либо вне самой программы, например, в стандартных библиотеках или в личных библиотеках групп программистов, работающих над данным проектом. Объектный код, созданный компилятором, обычно содержит «дыры» из-за этих отсутствующих частей. Компоновщик связывает объектный код с кодами отсутствующих функций, чтобы создать исполняемый загрузочный модуль (без пропущенных частей).

Если в проект входит хотя бы один файл C++, компоновка всегда осуществляется с помощью компилятора g++. Если же все файлы написаны на языке C, нужно использовать компилятор gcc. В нашем случае имеются файлы обоих типов, поэтому требуемая команда выглядит так:

%g++ -o reciprocal main.o reciprocal.o

Опция -o задает имя файла, создаваемого в процессе компоновки.

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

Запуск программы reciprocal осуществляют следующим образом:

%./reciprocal 7

The reciprocal of 7 is 0.142857

Как видите, компилятор g++ автоматически подключил к проекту стандартную библиотеку языка C, содержащую реализацию функции printf().

GNU-отладчик gdb

Отладчик – это программа, с помощью которой можно узнать, почему написанная вами программа ведет себя не так, как было задумано. Работать с отладчиком приходится очень часто. Большинство Unix-программистов имеет дело с GNU-отладчиком (GNU Debugger, GDB), который позволяет пошагово выполнять программу, создавать точки останова и проверять значения локальных переменных.

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

Чтобы можно было воспользоваться GNU-отладчиком, необходимо скомпилировать программу с включением в нее отладочной информации. Этой цели служит опция -g компилятора. Если имеется описанный выше файл Makefile, достаточно задать переменную CFLAGS равной -g при запуске утилиты make:

%make CFLAGS=-g

gсс -g -c main.c

g++ -g -c reciprocal.cpp

g++ -g -o reciprocal main.o reciprocal.o

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

Запуск отладчика

Отладчик gdb запускается следующим образом:

%gdb reciprocal

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

(gdb)

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

(gdb) run

Starting program: reciprocal

Program received signal SIGSEGV, Segmentation fault.

__strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0)

at strtol.c:287

287 strtol.c: No such file or directory.

(gdb)

Проблема заключается в том, что в функции main() не предусмотрены средства контроля ошибок. Программа ожидает наличия аргумента, а в данном случае его нет. Получение сигнала SIGSEGV означает крах программы. Отладчик определяет, что причина краха находится в функции __strtol_internal(). Эта функция является частью стандартной библиотеки, но ее исходный файл отсутствует. Вот почему появляется сообщение "No such file or directory". С помощью команды where можно просмотреть содержимое стека:

(gdb) where

#0 __strtol_internal (nptr=0x0, endptr=0x0, base=10, group=0)

at strtol.c:287

#1 0x40096fb6 in atoi (nptr=0x0) at../stdlib/stdlib.h:251

#2 0x804863e in main (argc=l, argv=0xbffff5e4) at main.c:8

Как нетрудно заметить, функция main() вызвала функцию atoi(), передав ей нулевой указатель, что и стало причиной ошибки.

С помощью команды up можно подняться по стеку на два уровня, дойдя до функции main():

(gdb) up 2

#2 0х804863е in main (argc=l, argv=0xbffff5e4) at main.c:8

8 i = atoi (argv[l]);

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

(gdb) print argv[l]

$2 = 0x0

Это подтверждает нашу догадку о том, что причина ошибки - передача функции atoi() указателя NULL.

Установка контрольной точки осуществляется посредством команды break:

(gdb) break main

Breakpoint 1 at 0x804862e: file main.c, line 8.

В данном случае контрольная точка размещена в первой строке функции main(). Давайте теперь заново запустим программу, передав ей один аргумент:

(gdb) run 7

Starting program: reciprocal 7

Breakpoint 1, main (argc=2, argv=0xbffff5e4) at main.c:8

8 i = atoi (argv[l]);

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

(gdb) next

9 printf ("The reciprocal of %d is %g\n", i,

reciprocal(i));

Если требуется узнать, что происходит внутри функции reciprocal(), воспользуйтесь командой step:

(gdb) step

reciprocal (i=7) at reciprocal.cpp:6

6 assert (i!= 0);

Иногда удобнее запускать отладчик gdb непосредственно из редактора Emacs, а не из командной строки. Для этого следует ввести в редакторе команду M-x gdb. Когда отладчик останавливается в контрольной точке, редактор Emacs автоматически открывает соответствующий исходный файл. Не правда ли, проще разобраться в происходящем, глядя на весь файл, а не на одну его строку?

Работа с qmake

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

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

Полноценный инструментарий для программирования (контейнеры, протоколы ввода-вывода, протоколы сети, XML, 2D и 3D графика, связка с OpenGL) в среде Unix/Linux предоставляет Qt designer.

Предыдущие версии Qt содержали утилиту tmake, которая позволяла довольно просто создавать make-файлы. Она изначально была реализована на языке Perl, и это накладывало на нее дополнительные ограничения - для использования необходимо было наличие интерпретатора языка Perl на компьютере, что, в свою очередь, снижало способность переносимости данной утилиты. По этой причине и была создана новая утилита qmake, которая вошла в поставку Qt начиная с версии 3.0. Примечательно, что новая утилита так же хорошо переносима, как и сам Qt. Утилита qmake полностью освобождает программиста от использования tmake, перенимая все ее возможности. Благодаря этому старые конфигурационные файлы для tmake могут интерпретироваться ей без проблем. Таким образом, тем, кто уже успел привыкнуть к tmake, вовсе не обязательно изучать новый синтаксис.

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

%qmake -project

Создать из.pro файла make-файл совсем не трудно, для этого нужно просто дать команду

%qmake -o Makefile file.pro

В qmake существует возможность создания файлов с расширением dsp (Developer Studio Project), которые могут быть загружены в Visual Studio. Это можно сделать при помощи команды:

%qmake -o file.dsp -t vcapp file.pro

Опция -t является так называемой шаблонной опцией и заставляет qmake использовать параметр vcapp, невзирая на то, что написано в самом файле проекта. Шаблоны нужны для определения типа проекта. Например, при создании make-файла для приложения нужно задать в файле проекта опцию template = app, а при создании библиотеки template должен быть равен lib. Значения vcapp и vclib имеют смысл только для работы в OC Windows.

Допускается передавать опции в командной строке qmake:

%qmake -o Makefile "TEMPLATE=vcapp" file.pro

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

Опция Назначение
HEADERS Передается список созданных заголовочных файлов
SOURCES Передается список созданных файлов реализации (с расширением срр)
FORMS Передается список файлов с расширением.ui. Эти файлы создаются программой QtDesigner и содержат описание интерфейса пользователя в формате XML.
LEXISOURCES Lex - это программа для синтаксического анализа. Используется для написания компиляторов.
YACCSOURCES Yacc (Yet another Compiler Compiler) - утилита создания компиляторов
TARGET Передается имя приложения. Если данное поле не заполнено, то название программы будет соответствовать имени проектного файла
CONFIG Задает опции, которые должен использовать компилятор
DESTDIR Задает путь, куда будет помещен готовый исполняемый модуль
DEFINES Здесь можно передать опции для компилятора. Например, это может быть опция помещения отладочной информации для debuger в исполняемый модуль
INCLUDEPATH Путь к каталогу,где содержатся заголовочные файлы. Этой опцией можно воспользоваться в случае, если уже есть готовые заголовочные файлы и вы хотите использовать их в текущем проекте
DEPENDPATH В данном разделе указываются зависимости, необходимые для компиляции
DEF_FILE Файл определения модуля, указывает, является ли файл DLL библиотекой или EXE файлом. Используется только для OC Windows.
RCFILE Файл ресурсов в OC Windows. Содержит растровые изображения, меню, диалоговые окна и др. Используется только для OC Windows.
RESFILE Откомпилированный файл ресурсов (присоединяется к исполняемому коду программы). Используется только для OC Windows.

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

TEMPLATE = арр

HEADERS += filel.h \

file2.h

SOURCES += main.cpp \

filel.cpp \

file2.cpp

TARGET = file

CONFIG += qt warn_on release

В первой строке задается тип программы, в данном случае это приложение (template = app). Во второй строке, в опции headers, перечисляются все заголовочные файлы, принадлежащие проекту. В опции sources перечисляются все файлы реализации проекта. Опция target определяет имя программы. config определяет опции, которые должен использовать компилятор в соответствии с подсоединяемыми библиотеками. Например, в нашем случае Qt указывает, что это Qt-приложение и используется библиотека Qt, warn_on означает, что компилятор должен выдавать как можно больше предупреждающих сообщений, release указывает, что приложение должно быть откомпилировано в окончательном варианте, без отладочной информации.

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

Далее создаем исполняемый файл

%make

Задание.

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

ü Создать в домашнем каталоге папку ~/First;

ü Создать в папке ~/First файлы main.c, first.cpp и first.hpp;

ü В текстовом редакторе KWrite описать код созданных файлов;

ü Сохраните отредактированные файлы.

ü Откройте терминал.

ü Перейдите в Вашу рабочую папку. Введите команду: %cd ~/First

ü Произвести компиляцию исходного кода и компоновку объектных файлов, как описано в теории;

ü Запустить программу;

ü Удалить из каталога ~/First созданные компилятором и компоновщиком объектные файлы;

ü Создать Makefile;

ü Получить исполняемый файл с помощью утилиты make;

ü Произвести перекомпиляцию с включенной оптимизацией;

ü Произвести перекомпиляцию с включением отладочной информации;

ü Получить исполняемый файл с помощью утилиты qmake;

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

Команды для работы в окне терминала:

сd.. -- перемещение на один каталог вверх по дереву каталогов

cd ~ -- перемещение в домашний каталог (~home)

cd / directory_name -- перемещение в каталог directory_name

cp -- копирование файла

cp old_file new_file

ls -- вывод содержимого текущего каталога

mkdir -- создание нового каталога

rm -- удаление указанных после команды файлов

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

Файл main.c и first.hpp для всех вариантов:

//main.c

#include <stdio.h>

#include <stdlib.h>

#include "first.hpp"

int main(int argc, char **argv)

{

double i;

i = atof(argv[1]);

printf("The result of %s is %g\n", argv[1],first(i));

return 0;

}

//first.hpp

#ifdef __cplusplus

extern "C" {

#endif

extern double first(double i);

#ifdef __cplusplus

}

#endif

 

  first.cpp   first.cpp
1. #include <cassert> #include <math.h> #include "first.hpp" double first(double i) { assert(i >-1); assert(i <1); return asin(i); } 5. #include <cassert> #include <math.h> #include "first.hpp" double first(double i) { assert(i >-1); assert(i <1); return acos(i); }
2. #include <cassert> #include <math.h> #include "first.hpp" double first(double i) { assert(i >0); return sqrt(i); } 6. #include <cassert> #include "first.hpp" double first(double i) { assert(i!=5); assert(i!=1); return (i-3)*(i+5)/(i-5)/(i-1); }
3. #include <cassert> #include "first.hpp" double first(double i) { return 180*i/3.14; } 7. #include <cassert> #include "first.hpp" double first(double i) { return 2*3.14*i; }
4. #include <cassert> #include "first.hpp" double first(double i) { assert(i >3); return i; } 8. #include <cassert> #include <math.h> #include "first.hpp" double first(double i) { assert(i >0); assert(i <3.14); return sqrt(sin(i)); }

 

Вопросы

1. Какие редакторы позволяют создать исходные тексты программ на языке C или C++?

2. Какова организация получения исполняемого файла на языке C и C++?

3. Какие расширения имеют файлы C и C++?

4. Для чего применяют подсвечивание синтаксиса и автоматическое форматирование в текстовых редакторах?

5. Объяснить назначение компилятора, компоновщика и загрузчика файлов.

6. Как задается уровень отладки программы?

7. Каков принцип автоматизации процесса получения исполнительного файла?

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

9. Какая команда позволит найти интерактивную документацию по работе с qmake?

10. Какова организация получения исполняемого файла с помощью утилиты qmake?

11. Какая команда создает файл проекта Microsoft Visual Studio (.dsp) с помощью утилиты qmake?

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

 

Лабораторная работа 2

Задания.

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

 

1. Создайте класс IntegerSet (множество целых). Каждый объект класса может вмещать целые числа в диапазоне от 0 до 100. Множество внутренне представлено как массив из нулей и единиц. Элемент массива а[i] равен 1, если целое i находится в множестве. Элемент массива а[j] равен 0, если целое j не находится в множестве. Конструктор по умолчанию инициализирует множество как пустое, т.е. множество, чье представление в виде массива содержит только нули. Напишите функции-элементы для типичных операций над множествами. Например, функцию-элемент unionOflntegerSet, которая создает третье множество, являющееся теоретико-множественным объединением двух существующих (т.е. элемент массива третьего множества устанавливается равным 1, если этот элемент равен 1 хотя бы в одном или обоих существующих множествах, и элемент массива третьего множества устанавливается равным 0, если этот элемент равен 0 в обоих существующих множествах). Напишите функцию-элемент interestionOflntegerSets, которая создает третье множество, являющееся теоретико-множественным пересечением двух существующих наборов (т.е. элемент массива третьего множества устанавливается равным 0, если этот элемент равен 0 в одном или обоих существующих множествах, и элемент массива третьего множества устанавливается равным 1, если этот элемент равен 1 в обоих существующих множествах). Напишите функцию-элемент setPrint, которая печатает множество в виде списка чисел, разделенных пробелами.

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

3. Разработать класс Matrix – матрица. Размерность матрицы задавать в конструкторе. Реализовать метод доступа к элементам массива. Реализовать операции сложения, вычитания, умножения и матрицы.

4. Разработать класс ArrayOfInt – массив целых чисел. Размерность массива задавать в конструкторе. Реализовать метод доступа к элементам массива. Реализовать метод Sum – вычисление суммы чисел в массиве и метод Sort – сортировки массива по возрастанию.

5. Разработать класс FileStream, инкапсулирующий работу с файлами через стандартную библиотеку Си (fopen, fclose, fprintf, fscanf, fread, fwrite и т.д.). Реализовать методы открытия и закрытия файла (отдельный метод Open и метод Create и конструктор с именем открываемого файла, закрывать – в деструкторе и отдельным методом Close) Реализовать методы Write и Read для int, double и char*. Обеспечить два режима записи – двоичный и текстовый. В текстовом режиме все числа записываются в виде текста, например, целое число 3987 записывается как последовательность символов ‘3987 ’ (преобразование можно сделать при помощи fprintf, например), а в двоичном - в виде последовательности двух байт: 0x93, 0x0f (при помощи fwrite).

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

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

8. Разработать класс Rectangular – прямоугольник. Класс имеет атрибуты длина (length) и ширина (width), каждый из которых по умолчанию равен 1. Реализовать операции нахождения периметра (perimeter) и площади (area) прямоугольника. Функция State должна проверять, что length и width – числа с плавающей запятой, находящиеся в пределах от 0.0 до 20.0. Организовать корректный вывод результатов.

9. Разработать класс Circle – окружность. Класс имеет атрибуты радиус (radius) и константу π (pi). Реализовать операции нахождения периметра (perimeter) и площади (area) окружности. Организовать корректный вывод результатов.

10. Разработать класс Complex – комплексные числа. Реализовать операции: сложения, умножения комплексных чисел, умножения реальных чисел на комплексные и наоборот, нахождения реальной и мнимой части числа.

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

12. Разработать класс Rational – выполнение арифметических действий с дробями. Используйте целые переменные для представления закрытых данных класса – числителя и знаменателя. Создайте функцию конструктор, которая позволяет объекту этого класса принимать начальные значения при его объявлении. Конструктор должен содержать значения по умолчанию. Создайте открытые функции-элементы для сложения, вычитания, перемножения и деления двух чисел Rational. Реализовать вывод результата в форме “a / b” и в форме с плавающей точкой.

 

Комментарии

 

Комментарий 1. Если размерность вектора/матрицы задается в конструкторе, то необходимо использовать динамическое выделение памяти. То есть в конструкторе либо в методе задания размера (SetLen) необходимо выделять память, например, таким образом:

 

1. void Digit::SetLen(int new_len)

2. {

3. char *tmp = new char [new_len];

4. if (value){

5. for (int i=0; i<min(len,new_len); i++)

6. tmp[i] = value[i];

7. delete [len]value;

8. }

9. value = tmp;

10. len = new_len;

11. }

В строке 3 выделяется место под новый массив (нового размера), в строках 4-8 происходит переприсваивание нового вектора в старый, причем в строках 5-6 старый массив копируется в новый. Обратите внимание, что количество копируемых элементов выбирается как минимальное между размером старого и нового векторов. Только после этого удаляется (строка 7) старый вектор вещественных чисел. Имея такой метод SetLen, можно написать следующий конструктор:

1. Digit::Digit(int size)

2. {

3. value = NULL;

4. len = 0;

5. SetLen(size);

6. }

В строках 3 и 4 обнуляются внутренние данные класса (это считается хорошим стилем программирования). Тогда при вызове функции SetLen гарантировано, что данные члены будут нулевыми. Это является принципиальным для функции SetLen. Дело в том, что данная функция выделяет память под новый массив в случае, если value == 0. Если же это значение отлично от нуля, то сначала освобождается память от предыдущего содержимого, то есть считается, что объект уже существовал, что в корне не верно для конструктора. Конструктор потому и называется конструктором, что он создает новый объект, то есть вызывается для вновь создаваемого объекта.

В случае, если в процессе работы необходимо изменить – увеличить или уменьшить размер массива, то необходимо вызвать метод SetLen, передавая ему количество элементов в новом массиве.

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

1. class Digit {

2. …

3. Digit operator+(const Digit&);

4. …

5. };

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

1. Digit a,b(31946);

2. a = b*Digit(35);

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

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

1. class Digit {

2. …

3. Digit& operator=(int s) {

4. ConvertFromInteger(s);

5. return *this;

6. }

7. Digit operator*(const Digit& s);

8. };

9. …

10. Digit a,b,c;

11. b = 31946;

12. c = 35;

13. a = b*c;

Обратите внимание, что оператор присваивания возвращает ссылку на сам объект. По парадигме, принятой в языке С и соответственно наследованной в языке С++, любой оператор, в том числе и оператор присваивания, возвращает какое-то значение, которое потом может использоваться при вычислении дальнейшего выражения. Метод ConvertFromInteger – некий внутренний метод, который переводит из целого числа в тип Digit. Однако делать так не совсем удачно с точки зрения удобства использования данного класса (см. строки 11, 12).

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

1. class Digit {

2. …

3. friend Digit operator*(Digit&, Digit&);

4. friend Digit operator*(Digit&, double);

5. friend Digit operator*(double, Digit&);

6. };

7. …

8. Digit operator*(Digit& a, Digit& b)

9. {

10. Digit tmp;

11. … // в tmp помещаем результат умножения a на b

12. return tmp;

13. }

14. Digit operator*(Digit& a, double b)

15. {

16. Digit t;

17. t.ConvertFromDouble(b);

18. return a*t;

19. }

20. Digit operator*(double a, Digit& b)

21. {

22. Digit t;

23. t.ConvertFromDouble(a);

24. return t*b;

25. }

26. …

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



Поделиться:


Последнее изменение этой страницы: 2016-08-14; просмотров: 130; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.147.103.8 (0.216 с.)