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



 

Цели:

ü изучить основные свойства класса;

ü методику написания алгоритмов с использованием основных свойств классов и перевода таких алгоритмов на язык программирования С++ и разработки соответствующего проекта в среде Visual C++ 6.0.

 

Свойства классов

 

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

 

Наследование классов

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

Объекты разных классов и сами классы могут находиться в отношении наследования, при котором формируется иерархия объектов, соответствующая заранее предусмотренной иерархии классов. Иерархия классов позволяет определять новые классы на основе уже имеющихся. Имеющиеся классы обычно называются базовыми (иногда порождающими или родительскими), а новые классы, формируемые основе базовых, - производными (порождёнными), иногда классами-потомками или наследниками. Производные классы «получают наследство» - данные и методы своих базовых классов – и, кроме того, могут пополняться собственными компонентами (данными и собственными методами). Наследуемые компоненты не перемещаются в производный класс, а остаются в базовых классах. Сообщение, обработку которого не могут выполнить методы производного класса, автоматически передаётся в базовый класс. Классы могут наследовать свойства и методы нескольких родительских классов одновременно. Это называется множественное наследование.

 

Форма записи класса-наследника:

Класс-наследник – x

Родительские классы – y, z

// Наследование свойств и методов одного класса y

class x: <спецификатор доступа (как правило, public)> y

{…};

// Множественное наследование

class x: <спецификатор доступа)> y, z

{…};

 

Пример.

Описать 2 класса. Первый класс «точка»: свойства – координаты точки, метод – ввод-вывод координат. Второй класс «вектор в трёхмерном пространстве»: свойства – три координаты, методы – ввод-вывод координат, определение длины вектора.

class Point

{

private:

      float x, y;

public:

      void get_x (float x1);

      void get_y (float y1);

      void print ( );

};

void Point : : get_x (float x1)

{ x=x1; } 

void Point : : get_y (float y1)

{ y=y1;}

void Point : : print ( )

{ printf (“{%f; %f}\n”, x, y); }

 

class V_3D : public Point

{

  float z;

public:

      void get_z (float z1);

      void print ();

      float len ();

};

void V_3D : : get_z (float z1)

{z=z1;}

void V_3D : : print ()

{print( “{%f;%f;%f}\n”,x, y, z); }

 float V_3D : : len ()

{ return (sqrt (( x*x + y*y + z*z));}

 

 

Полиморфизм

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

Формы полиморфизма:

1. Перегрузка стандартных функций и стандартных операций.

2. Реализация виртуальных функций.

3. Использование шаблонов.

 

1.2.1) Перегрузка стандартных операций.

 

Одной из привлекательных особенностей языка Си++ является возможность распространения действия стандартных операций на операнды, для которых эти операции первоначально в языке не предполагались. Например, если S1 и S2 – символьные строки, то их конкатенацию (соединение) удобно было бы обозначить как S1+ S2. Однако бинарная операция + в обычном контексте языка Си++ предназначена для арифметических операндов и не предусматривает строковых операндов. Никакой возможности распространить действие стандартной операции + на строки в виде символьных массивов или строковых констант в языке Си++ нет. Однако, если определить S1 и S2 как объекты некоторого класса, например, введённого в классе stroka, то для них можно ввести операцию +, выполняемую по таким правилам, которые заранее выбрал программист. Для этих целей язык Си++ позволяет распространить действие любой стандартной операции на новые типы данных - механизм перегрузки стандартных операций.

Чтобы появилась возможность использовать стандартную для языка Си++ операцию (например, «+» или «*») с необычными для неё данными, необходимо специальным образом определить её новое поведение. Это возможно, если хотя бы один из операндов является объектом некоторого класса, то есть введённого пользователем типа. В этом случае применяется механизм, во многом схожий с механизмом определения функций. Для распространения действия операции на новые пользовательские типы данных программист определяет специальную функцию, называемую «операция – функция». Формат определения операции – функции:

<тип возвращаемого значения> operator <знак операции>(<спецификация параметров операции – функции>)

{

<операторы тела операции – функции>

}

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

<тип возвращаемого значения> operator <знак операции>(<спецификация параметров операции – функции>)

И в прототипе, и в заголовке определения операции – функции используется ключевое слово operator , вслед за которым помещён знак операции.

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

Т operator * (Тx, Тy)

Определённая таким образом операция (в нашем примере операция «звёздочка») называется перегруженной, а сам механизм – перегрузкой или расширение действия стандартной операций языка Си++.

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

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

Рассмотрим конкретный пример по перегрузке стандартных операций.

Описать класс одномерных массивов, содержащих 10 элементов.

Свойства класса – массив;

Методы – ввод-вывод массива;

Перегружаемые операции – сложение массивов, операция присваивания

Опишем класс в головном файле mas. h

class m10

{

int a[10];

public:

      void get_a ();

      void print ();

      m10 operator + (m10 v1);

      );

};

void m10 : : get_a ()

{

int i;

for (i=0; i<=10-1; i++)

{ printf (“?”); scanf ( “ %i”, &a[i]); }

}

void m10 : : print ()

{

int i;

for (i=0; i<=10-1; i++)

{ printf (“ %i ”, a[i]); }

}

m10 m10 : : operator + (m10 v1)

{

int i; m10 t;

for (i=0; i<=10-1; i++)

t. a[i]= a[i] + v1. a[i]; // t будет содержать сумму двух массивов

return t;

}

m10 m10 : : operator = (m10 v)

{

int i; 

for (i=0; i<=10-1; i++)

a[i] = v. a[i];

return *this;

}

где this – это зарезервированное слово, обозначающее указатель на объект, предшествующий знаку перегружаемой операции. В данном случае «возвращается» *this. Это значение по адресу указателя this, то есть возвращается объект, предшествующий знаку операции.

Программа

#include “mas.h”

int main ()

{

m10 A, B, C;

A.get_a();

B.get_a();

C=A+B;

C.print ();

return 1;

}

 

1.2.2) Виртуальные функции.

 

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

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

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

1.2.3) Классы и шаблоны.

 

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

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

template <список параметров шаблона> определение функции

Здесь угловые скобки являются неотъемлемым элементом определения. Список параметров шаблона должен быть заключён именно в угловые скобки.

Аналогично определяется шаблон семейства классов:

template <список параметров шаблона> определение класса

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

Определение шаблона может быть только глобальным.

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

Следующий шаблон позволяет автоматически формировать классы векторов с указанными свойствами:

// TEMPLATE.VEC - шаблон векторов

template <class T> // Т – параметр шаблона

class Vector

{

T *data; // Начало одномерного массива

int size; // Количество элементов в массиве

public:

      Vector (int); // Конструктор класса vector

      ~Vector () { delete [ ] data; } // Деструктор

      // Расширение действия (перегрузка) операции “[ ]”:

      T& operator [ ] ( int i ) { return data [i];}

};

// Внешнее определение конструктора класса:

template <class T>

Vector <T>: : Vector (int n)

{

data=new T[n];

size=n;

};

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

<имя параметризованного класса> <фактические параметры шаблона> <имя объекта> (<параметры конструктора>);

В нашем случае определить вектор, имеющий восемь вещественных координат типа double, можно следующим образом:

Vector <double> Z(8);

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

// P11.CPP - формирование классов с помощью шаблона

#include “template.vec” // Шаблон классов «вектор»:

#include <iostream.h>

main ()

{ // Создаём объект класса «целочисленный вектор»:

Vector <int> X (5);

// Создаём объект класса «символьный вектор»:

Vector <char> C (5);

// Определяем компоненты векторов:

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

{ X[i]=i; C[i]= ‘A’ + i; }

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

cout << “ “ << X[i] << ‘ ‘ << C[i];

}

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

0 А 1 В 2 С 3 D 4 E

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

В списке параметров шаблона могут присутствовать формальные параметры, не определяющие тип, точнее – это параметры, для которых тип фиксирован:

 

//P12.CPP

#include <iostream.h>

template <class T, int size = 64>

class row

{

T *data;

int length;

public : row ()

      {

            length = size;

            data = new T[size];

      }

      ~ row () { delete [ ] data;}

      T& operator { } (int i)

      { return data [i];}

};

void main ()

{

row <float,8> rf;

row <int,8> ri;

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

{ rf [i] = i; ri [i] = i * i; }

for (I=0; I<8; i++)

cout << “ “ << rf[i] << ‘ ‘ << ri [i];

}

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

0 0 1 1 2 4 3 9 4 16 5 25 6 36 7 49

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

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

 

1. Климова Л.М. Си++. Практическое программирование. Решение типовых задач. – М.: КУДИЦ–ОБРАЗ, 2001.

2. Подбельский В.В. Язык Си++. – М.: Финансы и статистика, 1996.

3. Семакин И.Г., Шестаков А.П. Основы программирования: Учебник. – М.: Мастерство; НМЦ СПО; Высшая школа, 2001.

4. Шилдт Г. Полный справочник по С. 4-е издание. : Пер. с англ. – М.: Издательский дом «Вильямс», 2002.

5. Хьюз Дж., Мичтом Дж. Структурный подход к программир-ованию. М.: Мир 1980.

6. Стивен Прата. Язык программирования С++. Лекции и упражнения. К.: Издательство «ДиаСофт», 2001.

7. Дайнтбегов Д.М., Черноусов Е.А. Основы алгоритмизации и алгоритмические языки. М.: высшая школа 1992.

 


Дата добавления: 2019-11-25; просмотров: 175; Мы поможем в написании вашей работы!

Поделиться с друзьями:






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