Пример программы объектно ориентированного программирования

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

Мир состоит из объектов и сам — объект. Исследуя Мир, принято объекты классифицировать по каким-либо признакам. Выделяют классы животных и растений, планет и звезд, атомов и молекул, машин и механизмов, и многие, многие другие. Эта идея положена в основу объектно-ориентированного программирования.

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

Многие из используемых языков программирования (C++, Java, Python, Ruby и т. д.) в большей или меньшей степени поддерживают объектно-ориентированное программирование. Наиболее полно концепция ООП представлена в языке C++. Поэтому на нем написаны все примеры, приведенные ниже.

Классы

Любой объект принадлежит определенному классу. С точки зрения языка C++ класс — это тип, объект — это переменная.

Описать класс на языке C++ можно так:

Например, класс обыкновенных дробей может быть описан так:

class CFract < public: CFract()<>; CFract(int, int); CFract(const CFract&); ~CFract()<>; CFract& operator=(const CFract&); CFract& operator=(std::string); CFract operator*(const CFract&); CFract operator/(const CFract&); CFract operator+(const CFract&); CFract operator-(const CFract&); std::string GetStr(); private: int num; int den; protected: int SetDen(int); void Swap(int*, int*); int Nod(int, int); void Reduce(); >;

В секции public: объявляются свойства и методы доступные в самом классе, его потомках. Кроме того, можно обратиться к ним через объект класса, используя точечную запись: имяОбъекта.свойтво/метод. Объявление конструкторов, деструктора, переопределение операций должно располагаться в этой секции.В приведенном примере определены:

CFract()<>; — конструктор без параметров;

CFract(int, int); — конструктор с двумя параметрами: целые числа;

CFract(const CFract&); — копирующий конструктор.

две операции присваивания(перегрузка операций — тема отдельной статьи):

CFract operator*(const CFract&);

CFract operator/(const CFract&);

CFract operator+(const CFract&);

CFract operator-(const CFract&);

Обратите внимание, что при объявлении этих методов указаны только типы параметров. Имена понадобятся в определении методов.

Секция private: содержит свойства и методы доступные только в определении методов этого класса. В этой секции объявлены два поля: num(числитель) и den(знаменатель)

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

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

Конструктор без параметров определен внутри класса. там не предусмотрено ни каких действий.

Конструктор с параметрами:

CFract::CFract(int sourceNum, int sourceDen)

Может случится, что будет передан знаменатель меньше или равный нулю. Эту неприятность нужно обработать. В случае если передано отрицательное число, то нужно поменять знак и у числителя и у знаменателя, если передан 0, то вывести сообщение об ошибке. Для этого в protected объявим метод int SetDen(int).

int CFract::SetDen(int sourceDen) < if(sourceDen >0) return den = sourceDen; else < if(sourceDen == 0)< std::coutelse < num = -num; return den = -sourceDen; >> > 

Тогда конструктор с параметрами примет вид:

CFract::CFract(int sourceNum, int sourceDen)

Кроме метода проверки знаменателя нужен метод сокращения дроби Reduce. Добавим его объявление в секцию protected: , а определение выглядит так:

Для сокращения дроби нужно найти наибольший общий делитель числителя и знаменателя. Добавим метод Nod в секцию protected, а ее определение выглядит так:

int CFract::Nod(int sa, int sb) < int n, d; d = sb; n = (sa<0)? -sa : sa; if(n < d) Swap(&n, &d); while(d)< n = n - d; if(n < d) Swap(&n, &d); >return n; > 

Еще один метод, который необходим в дальнейшем. Он также используется в методе Nod — обмен значениями переменных Swap. Добавим его объявление в секцию protected и определим:

void CFract::Swap(int *a, int *b)

Следующий важный метод — операция присваивания:

CFract& CFract::operator=(const CFract& source)

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

Хорошо бы перегрузить операцию присваивания в классе CFract, чтобы иметь возможность читать дробь из строки, то есть чтобы была допустима запись:

Примерная реализация этого метода (не совсем очевидна; пожалуй это тема отдельной статьи):

CFract& CFract::operator=(std::string source) < int a, b; //числитель, знаменатель, счетчик std::string tmp; //временная переменная для числа bool flA; //флаг введен или нет числитель tmp = ""; b = 1; flA = false; for(auto s: source)< if((s >= '0') && (s catch(. ) < std::cout tmp = ""; flA = true; > > try < b = ((tmp != "") && (flA)) ? std::stoi(tmp) : 1; a = (!flA) ? std::stoi(tmp): 0; >catch(. ) < std::cout num = a; SetDen(b); return *this; >

Определение арифметических операций

Наиболее сложная из всех — операция сложения. Нужно найти наименьший общий знаменатель, дополнительные множители для дробей. Затем сложить произведения числителей на соответствующие дополнительные множители. Примерное определение операции сложения:

CFract CFract::operator+(const CFract& source) < CFract tmp; int tmpNod, tmpDen; tmpNod = Nod(den, source.den); tmpDen = (den * source.den) / tmpNod; tmp.num = num * tmpDen / this ->den + source.num * tmpDen / source.den; tmp.SetDen(tmpDen); tmp.Reduce(); return tmp; >

Остальные операции определить гораздо проще. В приложении приведены определения этих операций.

И последнее в этой заметке: объявление и определение принято писать в различных файлах: fract1.h — объявление класса, fract1.cpp — определение методов. Вот эти файлы:

файл fract1.h

#ifndef _FRACT1_H #define _FRACT1_H #include class CFract < public: CFract()<>; CFract(int, int); CFract(const CFract& source); ~CFract()<>; CFract& operator=(const CFract&); CFract& operator=(std::string); CFract operator*(const CFract&); CFract operator/(const CFract&); CFract operator+(const CFract&); CFract operator-(const CFract&); std::string GetStr(); private: int num; int den; protected: int SetDen(int sourceDen); void Swap(int *a, int *b); int Nod(int sa, int sb); void Reduce(); >; #endif

файл fract1.cpp

#include #include #include "fract1.h" CFract::CFract(int sourceNum, int sourceDen) < num = sourceNum; SetDen(sourceDen); >CFract::CFract(const CFract& source) < num = source.num; SetDen(source.den); >CFract& CFract::operator=(const CFract& source) < if(this == &source) return *this; num = source.num; SetDen(source.den); return *this; >CFract& CFract::operator=(std::string source) < int a, b; //числитель, знаменатель, счетчик std::string tmp; //временная переменная для числа bool flA; //флаг введен или нет числитель tmp = ""; b = 1; flA = false; for(auto s: source)< if((s >= '0') && (s catch(. ) < std::cout tmp = ""; flA = true; > > try < b = ((tmp != "") && (flA)) ? std::stoi(tmp) : 1; a = (!flA) ? std::stoi(tmp): 0; >catch(. ) < std::cout num = a; SetDen(b); return *this; > CFract CFract::operator*(const CFract& source) < CFract tmp; tmp.num = num * source.num; tmp.SetDen(den * source.den); tmp.Reduce(); return tmp; >CFract CFract::operator/(const CFract& source) < CFract tmp; tmp.num = num * source.den; tmp.SetDen(den * source.num); tmp.Reduce(); return tmp; >CFract CFract::operator+(const CFract& source) < CFract tmp; int tmpNod, tmpDen; tmpNod = Nod(den, source.den); tmpDen = (den * source.den) / tmpNod; tmp.num = num * tmpDen / this ->den + source.num * tmpDen / source.den; tmp.SetDen(tmpDen); tmp.Reduce(); return tmp; > CFract CFract::operator-(const CFract& source) < CFract tmp; int tmpNod, tmpDen; tmpNod = Nod(den, source.den); tmpDen = (den * source.den) / tmpNod; tmp.num = num * tmpDen / den - source.num * tmpDen / source.den; tmp.SetDen(tmpDen); tmp.Reduce(); return tmp; >int CFract::SetDen(int sourceDen) < if(sourceDen >0) return den = sourceDen; else < if(sourceDen == 0)< std::coutelse < num = -num; return den = -sourceDen; >> > std::string CFract::GetStr() < std::ostringstream s; s "; return s.str(); > void CFract::Swap(int *a, int *b) < int tmp = *a; *a = *b; *b = tmp; >int CFract::Nod(int sa, int sb) < int n, d; d = sb; n = (sa<0)? -sa : sa; if(n < d) Swap(&n, &d); while(d)< n = n - d; if(n < d) Swap(&n, &d); >return n; > void CFract::Reduce() < int tmp = Nod(num, den); num = num / tmp; SetDen(den / tmp); >//перегрузка метода вывода в поток " int main() < CFract a(-1, 2), b(-3, 4); CFract c; c = a + b; std::cout


Функция main нужна только для тестирования. Вообще ее следует определять в отдельном файле.

Источник

Читайте также:  Основной язык программирования чпу называется
Оцените статью