Объектно-ориентированное программирование на 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 нужна только для тестирования. Вообще ее следует определять в отдельном файле.
class (C++)
Ключевое слово class объявляет тип класса или определяет объект типа класса.
Синтаксис
[template-spec] class [ms-decl-spec] [tag [: base-list ]] < member-list >[declarators]; [ class ] tag declarators;
Параметры
спецификация шаблона
Необязательные спецификации шаблона. Дополнительные сведения см. в разделе Шаблоны.class
ключевое слово class ;ms-decl-spec
Необязательная спецификация класса хранения. Дополнительные сведения см. в ключевое слово __declspec.Тег
Имя типа, присваиваемое классу. Этот параметр tag становится зарезервированным ключевым словом в области класса. Тег является необязательным. Если он опущен, определяется анонимный класс. Дополнительные сведения см. в разделе Анонимные типы классов.base-list
Необязательный список классов или структур, от которых этот класс будет наследовать члены. Дополнительные сведения см. в разделе Базовые классы . Перед каждым именем базового класса или структуры может предшествовать описатель доступа (общедоступный, закрытый, защищенный) и виртуальный ключевое слово. Дополнительные сведения см. в таблице доступа к членам статьи Управление доступом к членам класса .список членов
Список членов класса. Дополнительные сведения см. в статье Общие сведения о членах класса .declarators
Список деклараторов, в котором указываются имена одного или нескольких экземпляров типа класса. Деклараторы могут включать списки инициализаторов, если все члены данных класса являются public . Это чаще встречается в структурах, члены данных которых по умолчанию являются public элементами, чем в классах. Дополнительные сведения см. в разделе Обзор деклараторов .Комментарии
Дополнительные сведения о классах в целом см. в следующих разделах:
Сведения об управляемых классах и структуре в C++/CLI и C++/CX см. в разделе Классы и структуры.
Пример
// class.cpp // compile with: /EHsc // Example of the class keyword // Exhibits polymorphism/virtual functions. #include #include using namespace std; class dog < public: dog() < _legs = 4; _bark = true; >void setDogSize(string dogSize) < _dogSize = dogSize; >virtual void setEars(string type) // virtual function < _earType = type; >private: string _dogSize, _earType; int _legs; bool _bark; >; class breed : public dog < public: breed( string color, string size) < _color = color; setDogSize(size); >string getColor() < return _color; >// virtual function redefined void setEars(string length, string type) < _earLength = length; _earType = type; >protected: string _color, _earLength, _earType; >; int main()class (C++)
The class keyword declares a class type or defines an object of a class type.
Syntax
[template-spec] class [ms-decl-spec] [tag [: base-list ]] < member-list >[declarators]; [ class ] tag declarators;
Parameters
template-spec
Optional template specifications. For more information, refer to Templates.class
The class keyword.ms-decl-spec
Optional storage-class specification. For more information, refer to the __declspec keyword.tag
The type name given to the class. The tag becomes a reserved word within the scope of the class. The tag is optional. If omitted, an anonymous class is defined. For more information, see Anonymous Class Types.base-list
Optional list of classes or structures this class will derive its members from. See Base Classes for more information. Each base class or structure name can be preceded by an access specifier (public, private, protected) and the virtual keyword. See the member-access table in Controlling Access to Class Members for more information.member-list
List of class members. Refer to Class Member Overview for more information.declarators
Declarator list specifying the names of one or more instances of the class type. Declarators may include initializer lists if all data members of the class are public . This is more common in structures, whose data members are public by default, than in classes. See Overview of Declarators for more information.Remarks
For more information on classes in general, refer to one of the following topics:
For information on managed classes and structs in C++/CLI and C++/CX, see Classes and Structs
Example
// class.cpp // compile with: /EHsc // Example of the class keyword // Exhibits polymorphism/virtual functions. #include #include using namespace std; class dog < public: dog() < _legs = 4; _bark = true; >void setDogSize(string dogSize) < _dogSize = dogSize; >virtual void setEars(string type) // virtual function < _earType = type; >private: string _dogSize, _earType; int _legs; bool _bark; >; class breed : public dog < public: breed( string color, string size) < _color = color; setDogSize(size); >string getColor() < return _color; >// virtual function redefined void setEars(string length, string type) < _earLength = length; _earType = type; >protected: string _color, _earLength, _earType; >; int main()