Сущность объектно-ориентированного подхода к программированию (Классы)
Основные идеи объектно-ориентированного подхода опираются на следующие положения:
- Программа представляет собой модель некоторого реального процесса, части реального мира.
- Модель реального мира или его части может быть описана как совокупность взаимодействующих между собой объектов.
- Объект описывается набором параметров, значения которых определяют состояние объекта, и набором операций (действий), которые может выполнять объект.
- Взаимодействие между объектами осуществляется посылкой специальных сообщений от одного объекта к другому. Сообщение, полученное объектом, может потребовать выполнения определенных действий, например, изменения состояния объекта.
- Объекты, описанные одним и тем же набором параметров и способные выполнять один и тот же набор действий, представляют собой класс однотипных объектов.
С точки зрения языка программирования класс объектов можно рассматривать как тип данного, а отдельный объект - как данное этого типа. Определение программистом собственных классов объектов для конкретного набора задач должно позволить описывать отдельные задачи в терминах самого класса задач (при соответствующем выборе имен типов и имен объектов, их параметров и выполняемых действий).
Таким образом, объектно-ориентированный подход предполагает, что при разработке программы должны быть определены классы используемых в программе объектов и построены их описания, затем созданы экземпляры необходимых объектов и определено взаимодействие между ними.
Классы объектов часто удобно строить так, чтобы они образовывали иерархическую структуру. Например, класс “Студент”, описывающий абстрактного студента, может служить основой для построения классов “Студент 1 курса”, “Студент 2 курса” и т.д., которые обладают всеми свойствами студента вообще и некоторыми дополнительными свойствами, характеризующими студента конкретного курса. При разработке интерфейса с пользователем программы могут использовать объекты общего класса “Окно” и объекты классов специальных окон, например, окон информационных сообщений, окон ввода данных и т.п. В таких иерархических структурах один класс может рассматриваться как базовый для других, производных от него классов. Объект производного класса обладает всеми свойствами базового класса и некоторыми собственными свойствами, он может реагировать на те же типы сообщений от других объектов, что и объект базового класса и на сообщения, имеющие смысл только для производного класса. Обычно говорят, что объект производного класса наследует все свойства своего базового класса.
Введение в классы
Одним из наиболее важных понятий С++ является класс. Класс - это механизм для создания новых типов.
Синтаксис описания класса похож на синтаксис описания структуры.
class Имя_класса
{
закрытые элементы - члены класса
public:
открытые элементы - члены класса
};
На что здесь следует обратить внимание?
Имя_класса с этого момента становится новым именем типа данных, которое используется для объявления объектов класса.
Члены класса - это переменные состояния и методы этого класса, иными словами членами класса могут быть как переменные, так и функции. Функции и переменные, объявленные внутри объявления класса, становятся членами этого класса. Функции-члены класса будем называть методами этого класса.
По умолчанию, все функции и переменные, объявленные в классе, становятся закрытыми (private). Т.е. они доступны только из других членов этого класса. Для объявления открытых членов класса используется ключевое слово public. Все функции-методы и переменные, объявленные после слова public, доступны и для других членов класса, и для любой другой части программы, в которой содержится класс.
Пример.
class _3d
{
double x, y, z;
public:
double mod ();
double projection (_3d r);
};
В С++ для создания объектов традиционно принято использовать ключевое слово class.
Хотя функции mod() и projection(_3d r) объявлены в _3d, они еще не определены. Для определения метода - члена класса нужно связать имя класса, частью которого является метод, с именем класса. Это достигается путем написания имени функции вслед за именем класса с двумя двоеточиями. Два двоеточия называют операцией расширения области видимости.
double _3d::mod ()
{
return sqrt (x*x + y*y +z*z);
}
double _3d::projection (_3d r)
{
return (x*r.x + y*r.y + z*r.z) / mod();
}
...
main()
{
_3d a, b;
double dPro, dMod;
dMod = a.mod();
dPro = b.projection(a);
}
Пример 1.
Реализовать класс для работы с линейным массивом, обеспечить:
- создание заполнением случайным образом или вводом с клавиатуры;
- сортировка выбором;
- вывод;
- поиск минимального и максимального элементов.
// программа ==Пример 1==
#include <iostream.h>
#include <stdlib.h>
#include <time.h>
class Massiv
{
int *a, n;
public:
void Sozd();
void Del();
void Vvod();
void Vvod_Sl();
void Print();
int Min(int ind); // ind - с какого элемента начинать искать минимальный
int Max(int ind);
void Sort_Viborom();
};
void main()
{
Massiv a; randomize();
a.Vvod_Sl();
a.Print();
a.Sort_Viborom();
a.Print();
a.Del();
}
void Massiv :: Sozd()
{
cout << "Количество элементов массива? "; cin >> n;
a = (int *) malloc (sizeof(int) * n);
}
void Massiv :: Del()
{
free(a);
}
void Massiv :: Vvod()
{
Sozd();
for (int i=0; i < n; i++)
{
cout << i << "-й элемент? ";
cin >> *(a+i);
}
}
void Massiv :: Vvod_Sl()
{
Sozd();
for (int i=0; i < n; i++)
{
*(a+i)= -200+random(1001);
}
}
void Massiv :: Print()
{
for (int i=0; i < n; i++)
{
cout << *(a+i) << " ";
}
cout << endl;
}
int Massiv :: Min(int ind)
{
for (int i=ind+1; i < n; i++)
if (a[i]<a[ind])
ind = i;
return ind;
}
int Massiv :: Max(int ind)
{
for (int i=ind+1; i < n; i++)
if (a[i]>a[ind])
ind = i;
return ind;
}
void Massiv :: Sort_Viborom()
{
for (int i=0; i < n-1; i++)
{
int m = Min(i);
int Vsp=a[i];
a[i]=a[m];
a[m]=Vsp;
}
}
|
Конструкторы и деструкторы
В С++ определены два специальных типа методов – конструкторы и деструкторы. Конструкторы предназначены для инициализации элементов данных объектов. Деструкторы – для их удаления.
Описание конструктора:
идентификатор_конструктора (список параметров) {тело конструктора}
Идентификатор конструктора должен совпадать с именем класса. У конструктора не указывается тип возвращаемого значения (даже void).
Для класса Massiv конструктор может быть объявлен так:
Massiv (int *b, int m)
{
n=m;
for (int i=0; i<n; i++)
a[i]=b[i];
}
Конструктор может иметь значения параметров по умолчанию.
Правила использования конструкторов:
- если конструктор не вызывается явно, то он вызывается автоматически при создании объекта с использованием значений параметров по умолчанию;
- если конструктор не описан явно, он генерируется транслятором автоматически.
Вызывать конструктор можно так:
имя_класса имя_объекта = имя_конструктора (список фактических параметров);
имя_конструктора имя_объекта (список фактических параметров);
Деструктор уничтожает объект класса и освобождает оперативную память, занимаемую его элементами данных. Деструктор представляет собой метод с именем, совпадающим с именем класса, перед которым стоит символ ~ (тильда). Деструктор не должен иметь ни параметров, ни типа возвращаемого значения.
Описание:
~имя_класса () {операторы;}
Например:
~Massiv () {};
Деструктор вызывается явно или неявно. Явно деструктор вызывается при необходимости уничтожения объекта.
Полиморфизм
Полиморфизм – многообразие форм. Это свойство, которое позволяет использовать одно имя для обозначения различных действий для одного или нескольких родственных классов.
К формам полиморфизма в С++ относятся:
- перегрузка функций и операций;
- виртуальные функции;
- шаблоны – обобщение функций.
Перегрузка функций и операций – это статический полиморфизм, т.к. связи между функциями устанавливаются на этапе компиляции.
Перегрузка операций
С++ позволяет переопределить действие большинства операций так, чтобы при использовании с объектами конкретного класса они выполняли заданные функции. Это дает возможность использовать собственные типы данных точно так же, как стандартные. Обозначения собственных операций вводить нельзя.
При перегрузке унарной операции операндом является вызывающий класс.
При перегрузке бинарной операции в качестве первого операнда выступает вызывающий класс, второй операнд задается в качестве аргумента.
Синтаксис:
тип_результата operator операция(формальные_параметры);
Например,
Massiv operator +(int k);
Пример 2.Реализовать класс для операций над числами в системе счисления с основанием р (1<p<10). Решить задачу: Дан массив чисел в р-ичной системе счисления. Найти НОД этих чисел.
// программа == Пример 2 ==
#include <iostream.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
class syst_sch
{
int p;
char *s;
long s10;
public:
syst_sch()
{
s=(char*) malloc (256);
}
~syst_sch()
{
free(s);
}
void printp()
{
cout<<"число в "<<p<<"-ной системе счисления: "<<s<<endl;
}
void print10()
{
cout<<"число в десятичной системе счисления: "<<s10<<endl;
}
void vvodp();
void vvod10();
void perevodpto10 ();
void perevod10top ();
// операции
syst_sch operator +(syst_sch b);
syst_sch operator -(syst_sch b);
syst_sch operator *(syst_sch b);
syst_sch operator /(syst_sch b);
syst_sch operator %(syst_sch b);
syst_sch operator =(syst_sch b);
int operator !=(syst_sch b);
int operator >(syst_sch b);
};
void main ()
{
int nArr, i;
syst_sch x, b, c, arr[50], nod;
cout << "введите количество элементов массива " ;
cin >> nArr;
cout << endl;
for ( i=0; i<nArr; i++)
{
arr[i].vvodp();
}
// нахождение наибольшего общего делителя
nod = arr[0];
for (i=1; i<nArr; i++)
{
x=arr[i];
while (x !=nod)
if (x>nod)
x -= nod;
else nod=nod-x;
}
cout << "наибольший общий делитель чисел массива равен " <<endl;
nod.printp();
nod.print10();
getch();
}
void syst_sch :: vvodp()
{
cout<<"введите p (основание системы счисления): ";
cin>>p;
cout<<"вв.число в р-ной системе счисления: ";
cin >>s;
perevodpto10 ();
}
void syst_sch :: vvod10()
{
cout<<"введите p (основание системы счисления): ";
cin>>p;
cout<<"вв.число в десятичной системе счисления: ";
cin >>s10;
perevod10top ();
}
void syst_sch :: perevodpto10 ()
{
int i=0;
s10=0;
while (s[i] != '\0')
{
s10 = s10*p+s[i]-'0';
i++;
}
}
void syst_sch :: perevod10top ()
{
int i=0;
long vsp = s10;
int st=1;
while (st < s10)
{
st *= p;
i++;
}
s[i]='\0';
i--;
while (vsp)
{
s[i] = vsp%p+'0';
vsp /= p;
i--;
}
}
syst_sch syst_sch :: operator = (syst_sch b)
{
p = b.p;
s10 = b.s10;
int i=0;
while (b.s[i] != '\0')
{s[i] = b.s[i]; i++; }
s[i] = b.s[i];
return *this;
}
syst_sch syst_sch :: operator +(syst_sch b)
{
int n, q, vsp;
syst_sch c; c.p=p;
c.s10=s10+b.s10;
c.perevod10top();
return c;
}
syst_sch syst_sch :: operator -(syst_sch b)
{ int n, q, vsp;
syst_sch c;
c.p=p;
c.s10=s10-b.s10;
c.perevod10top();
return c;
}
syst_sch syst_sch :: operator *(syst_sch b)
{
int n, q, vsp;
syst_sch c;
c.p=p;
c.s10=s10*b.s10;
c.perevod10top();
return c;
}
syst_sch syst_sch :: operator /(syst_sch b)
{
int n, q, vsp;
syst_sch c;
c.p=p;
c.s10=s10/b.s10;
c.perevod10top();
return c;
}
syst_sch syst_sch :: operator %(syst_sch b)
{
int n, q, vsp;
syst_sch c;
c.p=p;
c.s10=s10%b.s10;
c.perevod10top();
return c;
}
int syst_sch :: operator !=(syst_sch b)
{
return s10!=b.s10;
}
int syst_sch :: operator >(syst_sch b)
{
return s10>b.s10;
}
|
Пример 3. Описать класс для работы с неотрицательными длинными числами и реализовать следующие операции:
- ввод числа;
- вывод длинного числа;
- операции отношения для длинных чисел;
- сложение;
- вычитание;
- умножение;
- деление.
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#define MaxN 2000
typedef int DlCh[MaxN];
class DC
{ DlCh a;
void per();
public:
void Nul();
int KTs();
DC operator +(DC b); /* сложение */
DC operator =(DC b); /* присваивание */
DC operator *(DC b); /* умножение */
DC operator *(long b); /* умножение на короткое*/
void vvod();
void Print();
};
void main()
{DC a, b, c, d; int n;
/*a.vvod();
b.vvod();
c=a+b;
c.Print();
int n=100;
d=a; */ cout<<"n?"; cin >> n; d.Nul();
d.vvod();
for (int i=1;i<=n;i++)
d=d*n;
d.Print();
}
/* Обнуление длинного числа */
void DC::Nul()
{
for (int i=0;i<MaxN;i++) a[i]=0;
}
/* Ввод длинного числа в виде строки и преобразование его в массив цифр */
void DC::vvod()
{ char sa[MaxN];
cout <<"Введите длинное число: " << endl;
cin >> sa;
Nul();
int n=strlen(sa);
for (int i=0;i<n;i++) a[i]=sa[n-i-1]-'0';
}
/* Определение количества цифр в записи длинного числа */
int DC::KTs()
{int i;
for (i=MaxN-1; i>0&&a[i]==0;i--);
return i+1;
}
int maxi(int a, int b)
{
if (a>b) return a; else return b;
}
/* Операция сложения */
DC DC::operator +(DC b)
{int n, Vsp, p; DC c;
c.Nul();
n=maxi(KTs(), b.KTs());
p=0;
for (int i=0; i<=n; i++) {Vsp=a[i]+b.a[i]+p; p=Vsp/10; c.a[i]=Vsp%10;}
return c;
}
DC DC::operator *(DC b) /* умножение */
{int m, n, Vsp, p; DC c;
c.Nul();
m=KTs(); n=b.KTs();
for (int i=0; i<m; i++)
{ p=0;
for (int j=0; j<n; j++)
{Vsp=a[i]*b.a[j]+p+c.a[i+j];
c.a[i+j] = Vsp%10;
p= Vsp/10;
}
c.a[i+n]=p;
}
return c;
}
DC DC::operator =(DC b) /* присваивание */
{
for(int i=0;i<MaxN;i++) a[i]=b.a[i];
return *this;
}
/* Вывод длинного числа */
void DC::Print()
{
for (int i=KTs()-1;i>=0;i--) cout<<a[i];
cout<<"\n";
}
DC DC::operator *(int b) /* умножение */
{int m, n, Vsp, p; DC c;
c.Nul();
p=0;
for (int i=0;i<=KTs()-1;i++)
{Vsp=a[i]*b+p;
c.a[i]= Vsp%10;
p=Vsp/10;}
i=KTs();
while (p) {c.a[i]=p%10; p/=10; i++;}
return c;}
|
|