Программирование C++. Пензенский государственный университет введение в программирование на языке с лабораторный практикум пенза 2010 г 2
Скачать 1.47 Mb.
|
#include "vector1.h" const int N = 5; // Число векторов в массиве int main() { Создаем и инициализируем вектор v1 и массив векторов v_a, вызывается конструктор по умолчанию VECTOR1 v1, v_a[N]; Задаем значения и печатаем массив векторов for ( int i = 0; i < N; i++ ) { v_a[i].Assign( i, i+1 ); cout<< "\n Vector v_a[" < "]:" ; v_a[i].print( ); } // К элементам массива векторов добавляем единичные вектора // и печатаем массив for ( int i = 0; i < N; i++ ) { ++v_a[i]; cout<< "\n Vector v_a[" < "]:" ; v_a[i].print( ); } // Вычисляем сумму элементов массива векторов и печатаем ее for ( int i = 0; i < N; i++ ) { v1 = v1 + v_a[i]; } cout<< "\n Summa - vector v1 : " ; v1.print( ); cout< system( "pause" ); return 0; } 62 Результат работы программы Наследование и полиморфизм Наследование позволяет классам- потомкам при сохранении всех свойств классов- родителей добавлять свои собственные свойства, которые отражают их индивидуальность. На рисунке 4.1 приведена диаграмма классов Figure фигура, Circle окружность, Rectangle прямоугольник. Рисунок 4.1 – Диаграмма классов 63 Класс Figure – базовый класс, Circle и Rectangle – наследуемые классы. Полиморфизм в С имеет две формы перегрузка операций, функций, использование шаблонов. В этом случае определение конкретного экземпляра операции, функции или класса выполняется на этапе компиляции и называется статическим связыванием использование виртуальных функций (основная форма полиморфизма. В этом случае определение конкретного экземпляра операции, функции или класса выполняется вовремя выполнения программы и называется динамическим связыванием. Рассмотрим пример реализации диаграммы классов (рисунок 4.1) с использованием наследования и виртуалных функций. В примере вместо реальных графических методов будем использовать сообщения, выводимые на консоль. Пример Иерархия классов Фигура, Окружность, Прямоугольник Файл fiures.h // определение классов Фигура, Окружность, // Прямоугольник, их данных и методов #ifndef _FIGURES_H предотвращение многократного #define _FIGURES_H включения заголовочного файла #include using std::endl; using std::cout; class Figure // Базовый класс { // Методы public : Figure( void ) // Конструктор по умолчанию { cout << "The constructor Figure" << endl; } virtual Figure( void ) // Виртуальный деструктор { cout << "The destructor Figure" << endl; } // Нарисовать фигуру - чисто виртуальная функция // не имеет реализации в базовом классе virtual void draw( void ) = 0; }; class Circle: public Figure // Circle наследуется от Figure { // Данные private : int x, // координата центра y, // координата центра r; // Радиус окружности 64 // Методы public : Circle( int CenterX, int CenterY, int Radius ) // Конструктор { cout << "The constructor Circle" << endl; x = CenterX; y = CenterY; r = Radius; } virtual Circle( void ) // Виртуальный деструктор { cout << "The destructor Circle" << endl; } // Нарисовать фигуру - виртуальная функция virtual void draw( void ); }; // Нарисовать окружность, реализация в классе Circle void Circle::draw( void ) { cout << "The Circle draw" << endl; } class Rectangle: public Figure // Rectangle наследуется от Figure { // Данные private : int l, // Левый верхний t, // угол r, // Правый нижний угол b; // прямоугольника // Методы public : Rectangle( int L, int T, int R, int B ) // Конструктор { cout << "The constructor Rectangle" << endl; l = L; t = T; r = R; b = B; } virtual Rectangle( void ) // Виртуальный деструктор { cout << "The destructor Rectangle" << endl; } // Нарисовать фигуру - виртуальная функция virtual void draw( void ); }; // Нарисовать прямоугольник, реализация в классе Rectangle void Rectangle::draw( void ) { cout << "The Rectangle draw" << endl; } #endif _FIGURES_H 65 Файл der_virt1.cpp // Тестирование классов Фигура, Окружность, // Прямоугольник #include "figures.h" int main() // Тестирование классов Figure, Circle и Rectangle { // Создается массив указателей на базовый класс, который // инициализируется адресами объектов классов Circle и Rectangle Figure *figures[2]; figures[0] = new Circle( 100, 100, 10 ); figures[1] = new Rectangle( 100, 100, 200, 250 ); // Для каждого объекта базового класса вызывается метод рисования фигуры figures[0]->draw(); figures[1]->draw(); // Уничтожаются созданные объекты. Если бы деструкторы небыли виртуальными, то при уничтожении объектов вызывался бы Figure( ) - // деструктор базового класса, что приводило бык ошибке delete figures[0]; delete figures[1]; cout< system( "pause" ); return 0; } Результат работы программы Обратите внимание на порядок вызова конструкторов сначала базового, а потом наследуемого класс с аи де стр ук торов сначала наследуемого класс с а , а потом базового. Использование виртуальных функций позволяет с помощью указателя на объект базового класса вызывать методы наследуемых классов (в примере draw ) – это и есть полиморфизм 66 Примеры программ 4.4.1 Класс Одномерный динамический массив Данный класс содержит одномерный массив целых чисел, память для которых выделяется динамически. Использование динамической памяти требует глубинного (поэлементного) копирования в операциях присваивания с объектами данного класса. Поэтому в классе необходимо создать копирующий конструктор и перегрузить операцию присваивания. Для того, чтобы обеспечить повторное присваивание например, a=b=c ) перегружаемая функция должна возвращать ссылку В классе реализуется безопасная операция индексирования, которая предотвращает обращение к элементам массива, номер которых выходит заграницы массива. Для того, чтобы операцию индексирования можно было использовать со бе их сторон при сваи в ни я , перегружаемая функция должна возвращать ссылку, ан е значение Для проверки корректности выполнения программы в реализации методов класса используется макрос выражение) из стандартной библиотеки assert.h. Если результат выражения, стоящего в скобках – ложь, то выполнение программы прерывается и выдается дианостическое сообщение. Пример Класс Одномерный динамический массив Файл DinArray1.h определение динамического массива и его операций Н предотвращение многократного #define Н включения заголовочного файла #include "assert.h" #include using std::endl; using std::cout; class Array1D динамический одномерный массив { Данные private : int *p; указатель на первый элемент массива целых чисел int size; размерность массива Методы public : Array1D( int n=3); конструктор по умолчанию(размерность массива = 3) Array1D( const Array1D& a); копирующий конструктор Array1D() { delete [] p;} //деструктор освобождает память, выделенную для массива void print() const ; вывод значений элементов массива перегружаемые операции int & operator []( int i) const ; индексирование Array1D& operator =( const Array1D& a); присваивание Array1D operator +( const Array1D& a) const ; сложение (бинарная операция) }; 67 реализация методов класса Array1D Array1D::Array1D( int n) : size(n) конструктор по умолчанию assert(n>0); размерность массива должна быть положительной p= new int [n]; //выделяем память для элементов массива assert(p!=0); //если память не выделена, то аварийное завершение }; Array1D::Array1D( const Array1D& a) : size(a.size) копирующий конструктор p= new int [size]; выделяем память для элементов массива assert(p!=0); //если память не выделена, то аварийное завершение i=0; i p[i] = a.p[i]; присваиваем значения элементам массива void Array1D::print() const { for ( int i=0; i cout< cout< } int & Array1D:: operator []( int i) const { assert( i>=0 && i если индекс заграницами, то аварийное завершение p[i]; возвращаем ссылку на i – й элемент массива Array1D& Array1D:: operator =( const Array1D& a) { if ( this != &a){ если присваивание самому себе, то ничего делать не надо == a.size); если размеры массивов неравны, то аварийное завершение i=0; i p[i] = a.p[i]; } return * this ; возвращаем ссылку на объект Array1D } Array1D Array1D:: operator +( const Array1D& a) const { assert(size == a.size); если размеры массивов неравны, то аварийное завершение sum(size); создаем массив sum размерности size for ( int i=0; i sum.p[i] = p[i] + a.p[i]; // или sum.p[i]=this->p[i] +a.p[i]; return sum; сначала вызывается конструктор копирования потом деструктор массива sum e } #endif Н 68 Файл DinArray1.cpp // Тестирование класса Array1D #include "DinArray1.h" int main() { Array1D a, b, c; Размерность массивов по умолчанию = 3 for ( int i=0; i<3; ++i) a[i]=i+1; Инициализируем массива и выводим его значения a.print(); b = a; Присваиваем элементам массива b значения элементов массива a cout<< "Array1D b: " < b.print(); a = a + b + (c = a + b); Сначала определяем значения с, затем увеличиваем значения а cout<< "Array1D c: " < c.print(); cout<< "Array1D a: " < a.print(); system( "pause" ); return 0; Результат работы программы 69 4.4.2 Класс Динамически размещаемая срока Данный класс содержит строку произвольной длины, символы которой размещаются в памяти динамически. В реализации методов класса используются функции из стандартной библиотеки < cstring>: strcpy_s(s, len, str) – копирование не более len символов из строки в строку strcat_s(s, len, str) – добавление не более len символов из строки в строку strlen(str) – длина строки str. Функция возвращает значение типа Функции strcpy_s, strcat_s безопасные (контролируют число пересылаеых символов) по сравнению с функциями strcpy, strcat, при использовании котрых компилятор выдает предупреждение «This function or variable may be unsafe. Consider using strcpy_s instead.» Для проверки корректности выполнения программы в реализации методов класса используется макрос выражение) из стандартной библиотеки Пример Класс Динамически размешаемая строка Файл DinString.h определение динамической строки и операций Н предотвращение многократного #define Н включения заголовочного файла #include using std::endl; using std::cout; #include #include "assert.h" class DinString динамически размещаемая срока { Данные private : char *s; указатель на строку int len; длина строки Методы public : DinString(): len(0){ конструктор по умолчанию s= new char [1]; assert(s!=0); s[0]=0; } DinString( const DinString& str); копирующий конструктор DinString( const char * str); преобразующий конструктор - преобразует тип char* к типу DinString DinString() { delete [] s;} //деструктор освобождает память, // выделенную для строки void print() const { вывод строки // перегружаемые операции DinString& operator =( const DinString& str); присваивание DinString operator + ( const DinString& str) const ; конкатенация }; 70 реализация методов класса DinString DinString::DinString( const char * str) преобразующий конструктор { len = преобразуем возвращаемое значение // функции strlen типа size_t в // значение типа int s= new char [len+1]; выделяем память для строки assert(s!=0); если память не выделена, то аварийное завершение strcpy_s(s, len+1, str); } DinString::DinString( const DinString& str) : len(str.len) копирующий конструктор { s= new char [len+1]; выделяем память для строки assert(s!=0); если память не выделена, то аварийное завершение strcpy_s(s, len+1, str.s); } DinString& DinString:: operator =( const DinString& str) { if ( this != &str){ если присваивание самому себе, то ничего делать не надо delete [] s; удаляем "старую" строку len = str.len; формируем длину новой строки s= new char [len+1]; выделяем память для строки assert(s!=0); если память не выделена, то аварийное завершение strcpy_s(s, len+1, str.s); } return * this ; возвращаем ссылку на объект } DinString DinString:: operator + ( const DinString& str) const { DinString tmp; tmp.s = new char [len + str.len +1]; tmp.len=len + str.len; |