Модуль 3: Основные понятия объектно-ориентированного
программирования.
Темы лекции: Продолжение введения в ООП.Перегрузка
операций.
Практическое задание: Перегрузка операций.
Тренер: Игорь Шкулипа, к.т.н.
C++ Базовый. Занятие 9
http://www.slideshare.net/IgorShkulipa 2
Продолжение введения в ООП
http://www.slideshare.net/IgorShkulipa 3
Дружественные функции
Дружественные функции – это функции, объявленные вне класса,
но имеющие доступ к закрытым и защищенным полям данного класса
Дружественная функция объявляется внутри класса с модификатором
friend
Дружественные функции не являются членами класса, поэтому им не
передается указатель this
Дружественные операции, как и дружественные функции, могут
иметь доступ к приватным и защищенным методам класса
class Class1
{
friend void FriendMethod();
};
http://www.slideshare.net/IgorShkulipa 4
Дружественные классы
Некоторым классам может понадобиться доступ к закрытым данным друг
друга.
В этом случае необходимо объявить дружественный класс внутри
определения класса
class Class1;
class Class2
{
friend class Class1;
private:
int data;
};
Дружественная связь между классами является самой сильной.
Реализации классов оказываются связанными, что противоречит
принципу инкапсуляции.
(!) Не используйте дружественные классы до тех пор, пока их
использование не окажется единственным способом решения задачи
http://www.slideshare.net/IgorShkulipa 5
Статические данные и методы
Для каждого объекта создается своя собственная копия членов данных.
Для некоторых классов было бы удобно использовать данные, общие
для всех экземпляров данного класса (например, строковое
представление имени класса или константы, общие для всех
экземпляров класса, область видимости которых должна быть
ограничена методами класса)
Такие поля и методы называют статическими и объявляют при помощи
ключевого слова static
class Class1
{
static void StaticMethod();
};
http://www.slideshare.net/IgorShkulipa 6
Особенности статических методов
◦ Статические методы не получают указатель this.
◦ Статические методы могут обращаться только к статическим данным
класса.
◦ Статические методы могут вызывать только статические методы
(либо нестатические, если им передается указатель или ссылка на
объект класса).
◦ Статические методы имеют доступ к закрытым и защищенным полям
и методам класса, через экземпляры классов.
◦ Доступ к статическим методам и данным класса осуществляется по
имени класса (создавать экземпляр не требуется).
http://www.slideshare.net/IgorShkulipa 7
Применение статических методов
◦ Паттерн «одиночка» (singleton).
◦ Объект с глобальным доступом, существующий в программе в
единственном экземпляре.
◦ Методы и данные, характерные для класса в целом, а не для
отдельных его экземпляров.
◦ Создание классов-утилит
http://www.slideshare.net/IgorShkulipa 8
Вложенное объявление классов
Язык C++ позволяет разместить объявление одного класса (или другого
типа данных) внутри объявления другого.
Это полезно, когда вложенный тип данных используется только внешним
классом, или совместно с ним.
Использование вложенного класса может происходить двумя способами:
⚫ Из методов внешнего класса – по имени вложенного класса
⚫ Снаружи – при помощи указания имени внешнего класса:
ExternalClass::InternalClass
http://www.slideshare.net/IgorShkulipa 9
Пример вложенного объявления классов
class ExternalClass
{
public:
class InternalClass
{
public: void InternalMethod();
};
private:
void ExternalMethod()
{
InternalClass inClass;
inClass.InternalMethod();
}
};
void main()
{
ExternalClass::InternalClass inClass;
inClass.InternalMethod();
}
http://www.slideshare.net/IgorShkulipa 10
Шаблоны проектирования (паттерны)
Шаблон проектирования – это архитектурная конструкция,
представляющая собой решение проблемы проектирования в
рамках некоторого часто возникающего контекста.
http://www.slideshare.net/IgorShkulipa 11
Шаблон «Одиночка» (Singleton)
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton* GetInstance()
{
if (_instance==NULL) _instance= new Singleton();
return _instance;
}
void Method1(){cout<<"Method1n";}
void Method2(){cout<<"Method2n";}
private:
Singleton(){}
static Singleton* _instance;
};
Singleton::_instance=NULL;
int main()
{
Singleton* singleton = Singleton::GetInstance();
singleton->Method1();
singleton->Method2();
}
http://www.slideshare.net/IgorShkulipa 12
Применение Singleton
Применяется, когда нужен только один экземпляр класса.
Например для хранения глобальной конфигурации системы,
для ведения логов, связи с базой данных и т.д.
Основное преимущество перед глобальными переменными в
том, что экземпляр класса создается не при инициализации
программы, а по первому требованию.
http://www.slideshare.net/IgorShkulipa 13
Перегрузка операций
http://www.slideshare.net/IgorShkulipa 14
Для чего нужна перегрузка операций
Для некоторых типов данных естественными может оказаться
использование операций над базовыми типами:
⚫ += и + для конкатенации строк
⚫ -- и ++ для итераторов
⚫ арифметические операции для векторов и комплексных
чисел
⚫ [] для векторов и матриц
⚫ = для классов с собственным конструктором копирования
⚫ операции сравнения для строк и других типов
http://www.slideshare.net/IgorShkulipa 15
Перегрузка операций
Для пользовательских типов данных C++ позволяет задать
собственные операции
⚫ Некоторые из них всегда определяются внутри класса
⚫ =, +=, -=, *= и т.д.
⚫ Некоторые – снаружи (операции, в которых применяются
базовые типы).
⚫ Некоторые – где угодно.
Синтаксис:
<тип> operator <О>(параметры)
http://www.slideshare.net/IgorShkulipa 16
Ограничения
◦ Приоритет операций над пользовательскими типами тот же,
что и для базовых типов
◦ Нельзя переопределить операции точка (.) и sizeof
◦ Бинарные операции остаются бинарными, унарные -
унарными
http://www.slideshare.net/IgorShkulipa 17
Пример
class Complex
{
private:
double _im;
double _re;
public:
Complex();
Complex(double );
Complex(double, double);
~Complex();
}
Задача: выполнить перегрузку операций для нового типа
данных.
http://www.slideshare.net/IgorShkulipa 18
Класс «Комплексное число»
class Complex
{
private:
double _im;
double _re;
public:
Complex();
Complex(double );
Complex(double, double);
Complex (Complex &c);
~Complex();
Complex operator = (Complex &c)
{ this->SetIm(c.GetIm()); this->SetRe(c.GetRe()); return (*this); }
friend bool operator ==(const Complex& left, const Complex& right);
friend Complex operator +(const Complex& left, const Complex& right);
friend Complex operator -(const Complex& left, const Complex& right);
friend Complex operator *(const Complex& left, const Complex& right);
double GetRe(){return _re;}
double GetIm(){return _im;}
void SetRe(double re){_re=re;}
void SetIm(double im){_im=im;}
}
http://www.slideshare.net/IgorShkulipa 19
Конструкторы и деструктор
Complex::Complex()
:_re(0), _im(0)
{
}
Complex::Complex(double re)
:_re(re), _im(0)
{
}
Complex::Complex(double re, double im)
{
_re=re; _im=im;
}
Complex::Complex (Complex &c)
{
_re=c.GetRe(); _im=c.GetIm();
}
Complex::~Complex()
{
}
http://www.slideshare.net/IgorShkulipa 20
Операция сравнения
bool operator ==(const Complex& left, const Complex& right)
{
if ((left.GetRe()==right.GetRe()) &&
(left.GetIm()==right.GetIm()))
{
return true;
}
else
{
return false;
}
}
http://www.slideshare.net/IgorShkulipa 21
Арифметические операции
Complex operator +(const Complex& left, const Complex& right)
{
Complex temp;
temp.SetRe(left.GetRe() + right.GetRe());
temp.SetIm(left.GetIm() + right.GetIm());
return temp;
}
Complex operator -(const Complex& left, const Complex& right)
{
Complex temp;
temp.SetRe(left.GetRe() - right.GetRe());
temp.SetIm(left.GetIm() - right.GetIm());
return temp;
}
Complex operator *(const Complex& left, const Complex& right)
{
Complex temp;
double re=left.GetRe()*right.GetRe() – left.GetIm()*right.GetIm();
double im=left.GetRe()*right.GetRe() + left.GetIm()*right.GetIm();
temp.SetRe(re);
temp.SetIm(im);
return temp;
}
http://www.slideshare.net/IgorShkulipa 22
Потоки ввода-вывода
cin и cout - объекты классов istream (от Input Stream - поток
ввода) и ostream (от Output Stream - поток вывода)
соответственно. Именно для этих классов перегружены
операторы извлечения и вставки (<< и >>).
Базовым классом является класс ios (от Input/Output Stream -
потоковый ввод/вывод). У класса ios довольно много
производных классов.
Наследниками istream и ostream являются ifstream (от Input
File Stream) и ofstream (от Output File Stream). Которые
используются для ввода/вывода в файлы.
Кроме того, есть ещё один класс - fstream (от File Stream -
фаловый поток), в котором объединены возможности ifstream
и ofstream. fstream наследуется одновременно и от istream,
и от ostream.
http://www.slideshare.net/IgorShkulipa 23
Перегрузка операторов ввода-вывода
class Complex
{
... // Расширение класса Complex
friend ostream operator <<(ostream& out, Complex& c);
friend istream operator >>(istream& in, Complex& c);
}
ostream &operator<<(ostream &out, Complex &c)
{
if (c.GetIm()==0) out<<c.GetRe()<<“n”;
if (c.GetIm()>0) out<<c.GetRe()<<“+”<<c.GetIm()<<“i”<<“n”;
if (c.GetIm()<0) out<<c.GetRe()<<“-”<<-c.GetIm()<<“i”<<“n”;
return out;
}
istream &operator>>(istream &in, Complex &c)
{
double re, im;
in >> re >> im;
c.SetRe(re); c.SetIm(im);
return in;
}
http://www.slideshare.net/IgorShkulipa 24
Результат
#include “complex.h”
void main()
{
Complex c1(1,2);
Complex c2(2,3);
cout << c1;
cout << c2;
cin >> c1;
cin >> c2;
cout << c1+c2;
cout << c1-c2;
cout << c1*c2;
}
http://www.slideshare.net/IgorShkulipa 25
Лабораторная работа №9. Перегрузка операций
Создать класс «Вектор» произвольного размера.
Реализовать методы:
⚫ Конструкторы и деструктор
⚫ Доступа к данным
⚫ Вычисления модуля вектора
Выполнить перегрузку операций:
⚫ Сложения, вычитания, скалярного умножения векторов
⚫ Умножения вектора на скаляр
⚫ Сравнения векторов (==, !=)
⚫ Операции присваивания (=)
⚫ Операцию индексации ( [ ] )
⚫ Операции ввода-вывода
Создать приложение для демонстрации работы класса.
Меню приложения реализовать в виде класса, согласно паттерну
Singleton.
http://www.slideshare.net/IgorShkulipa 26
Бонусный слайд №1. Заголовок класса «Вектор»
class Vector {
private:
double* _data; int _size;
public:
Vector();
Vector(int);
Vector(double*, int);
Vector(Vector&);
~Vector();
Vector operator = (Vector &v) {//Код оператора}
friend bool operator ==(const Vector& left, const Vector& right);
friend bool operator !=(const Vector& left, const Vector& right);
friend Vector operator +(const Vector& left, const Vector& right);
friend Vector operator -(const Vector& left, const Vector& right);
friend double operator *(const Vector& left, const Vector& right);
friend Vector operator *(const Vector& vect, double scalar);
friend ostream operator <<(ostream& out, Vector& v);
friend istream operator >>(istream& in, Vector& v);
double operator[](int i);
int GetSize();
double* GetData();
void SetSize(int);
void SetData(double*);
}
http://www.slideshare.net/IgorShkulipa 27
Бонусный слайд №2. Операция индексации
double Vector::operator[](int i)
{
if(i<_size)
{
return _data[i];
}
else
{
return NULL;
}
}

C++ Базовый. Занятие 09.

  • 1.
    Модуль 3: Основныепонятия объектно-ориентированного программирования. Темы лекции: Продолжение введения в ООП.Перегрузка операций. Практическое задание: Перегрузка операций. Тренер: Игорь Шкулипа, к.т.н. C++ Базовый. Занятие 9
  • 2.
  • 3.
    http://www.slideshare.net/IgorShkulipa 3 Дружественные функции Дружественныефункции – это функции, объявленные вне класса, но имеющие доступ к закрытым и защищенным полям данного класса Дружественная функция объявляется внутри класса с модификатором friend Дружественные функции не являются членами класса, поэтому им не передается указатель this Дружественные операции, как и дружественные функции, могут иметь доступ к приватным и защищенным методам класса class Class1 { friend void FriendMethod(); };
  • 4.
    http://www.slideshare.net/IgorShkulipa 4 Дружественные классы Некоторымклассам может понадобиться доступ к закрытым данным друг друга. В этом случае необходимо объявить дружественный класс внутри определения класса class Class1; class Class2 { friend class Class1; private: int data; }; Дружественная связь между классами является самой сильной. Реализации классов оказываются связанными, что противоречит принципу инкапсуляции. (!) Не используйте дружественные классы до тех пор, пока их использование не окажется единственным способом решения задачи
  • 5.
    http://www.slideshare.net/IgorShkulipa 5 Статические данныеи методы Для каждого объекта создается своя собственная копия членов данных. Для некоторых классов было бы удобно использовать данные, общие для всех экземпляров данного класса (например, строковое представление имени класса или константы, общие для всех экземпляров класса, область видимости которых должна быть ограничена методами класса) Такие поля и методы называют статическими и объявляют при помощи ключевого слова static class Class1 { static void StaticMethod(); };
  • 6.
    http://www.slideshare.net/IgorShkulipa 6 Особенности статическихметодов ◦ Статические методы не получают указатель this. ◦ Статические методы могут обращаться только к статическим данным класса. ◦ Статические методы могут вызывать только статические методы (либо нестатические, если им передается указатель или ссылка на объект класса). ◦ Статические методы имеют доступ к закрытым и защищенным полям и методам класса, через экземпляры классов. ◦ Доступ к статическим методам и данным класса осуществляется по имени класса (создавать экземпляр не требуется).
  • 7.
    http://www.slideshare.net/IgorShkulipa 7 Применение статическихметодов ◦ Паттерн «одиночка» (singleton). ◦ Объект с глобальным доступом, существующий в программе в единственном экземпляре. ◦ Методы и данные, характерные для класса в целом, а не для отдельных его экземпляров. ◦ Создание классов-утилит
  • 8.
    http://www.slideshare.net/IgorShkulipa 8 Вложенное объявлениеклассов Язык C++ позволяет разместить объявление одного класса (или другого типа данных) внутри объявления другого. Это полезно, когда вложенный тип данных используется только внешним классом, или совместно с ним. Использование вложенного класса может происходить двумя способами: ⚫ Из методов внешнего класса – по имени вложенного класса ⚫ Снаружи – при помощи указания имени внешнего класса: ExternalClass::InternalClass
  • 9.
    http://www.slideshare.net/IgorShkulipa 9 Пример вложенногообъявления классов class ExternalClass { public: class InternalClass { public: void InternalMethod(); }; private: void ExternalMethod() { InternalClass inClass; inClass.InternalMethod(); } }; void main() { ExternalClass::InternalClass inClass; inClass.InternalMethod(); }
  • 10.
    http://www.slideshare.net/IgorShkulipa 10 Шаблоны проектирования(паттерны) Шаблон проектирования – это архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.
  • 11.
    http://www.slideshare.net/IgorShkulipa 11 Шаблон «Одиночка»(Singleton) #include <iostream> using namespace std; class Singleton { public: static Singleton* GetInstance() { if (_instance==NULL) _instance= new Singleton(); return _instance; } void Method1(){cout<<"Method1n";} void Method2(){cout<<"Method2n";} private: Singleton(){} static Singleton* _instance; }; Singleton::_instance=NULL; int main() { Singleton* singleton = Singleton::GetInstance(); singleton->Method1(); singleton->Method2(); }
  • 12.
    http://www.slideshare.net/IgorShkulipa 12 Применение Singleton Применяется,когда нужен только один экземпляр класса. Например для хранения глобальной конфигурации системы, для ведения логов, связи с базой данных и т.д. Основное преимущество перед глобальными переменными в том, что экземпляр класса создается не при инициализации программы, а по первому требованию.
  • 13.
  • 14.
    http://www.slideshare.net/IgorShkulipa 14 Для чегонужна перегрузка операций Для некоторых типов данных естественными может оказаться использование операций над базовыми типами: ⚫ += и + для конкатенации строк ⚫ -- и ++ для итераторов ⚫ арифметические операции для векторов и комплексных чисел ⚫ [] для векторов и матриц ⚫ = для классов с собственным конструктором копирования ⚫ операции сравнения для строк и других типов
  • 15.
    http://www.slideshare.net/IgorShkulipa 15 Перегрузка операций Дляпользовательских типов данных C++ позволяет задать собственные операции ⚫ Некоторые из них всегда определяются внутри класса ⚫ =, +=, -=, *= и т.д. ⚫ Некоторые – снаружи (операции, в которых применяются базовые типы). ⚫ Некоторые – где угодно. Синтаксис: <тип> operator <О>(параметры)
  • 16.
    http://www.slideshare.net/IgorShkulipa 16 Ограничения ◦ Приоритетопераций над пользовательскими типами тот же, что и для базовых типов ◦ Нельзя переопределить операции точка (.) и sizeof ◦ Бинарные операции остаются бинарными, унарные - унарными
  • 17.
    http://www.slideshare.net/IgorShkulipa 17 Пример class Complex { private: double_im; double _re; public: Complex(); Complex(double ); Complex(double, double); ~Complex(); } Задача: выполнить перегрузку операций для нового типа данных.
  • 18.
    http://www.slideshare.net/IgorShkulipa 18 Класс «Комплексноечисло» class Complex { private: double _im; double _re; public: Complex(); Complex(double ); Complex(double, double); Complex (Complex &c); ~Complex(); Complex operator = (Complex &c) { this->SetIm(c.GetIm()); this->SetRe(c.GetRe()); return (*this); } friend bool operator ==(const Complex& left, const Complex& right); friend Complex operator +(const Complex& left, const Complex& right); friend Complex operator -(const Complex& left, const Complex& right); friend Complex operator *(const Complex& left, const Complex& right); double GetRe(){return _re;} double GetIm(){return _im;} void SetRe(double re){_re=re;} void SetIm(double im){_im=im;} }
  • 19.
    http://www.slideshare.net/IgorShkulipa 19 Конструкторы идеструктор Complex::Complex() :_re(0), _im(0) { } Complex::Complex(double re) :_re(re), _im(0) { } Complex::Complex(double re, double im) { _re=re; _im=im; } Complex::Complex (Complex &c) { _re=c.GetRe(); _im=c.GetIm(); } Complex::~Complex() { }
  • 20.
    http://www.slideshare.net/IgorShkulipa 20 Операция сравнения booloperator ==(const Complex& left, const Complex& right) { if ((left.GetRe()==right.GetRe()) && (left.GetIm()==right.GetIm())) { return true; } else { return false; } }
  • 21.
    http://www.slideshare.net/IgorShkulipa 21 Арифметические операции Complexoperator +(const Complex& left, const Complex& right) { Complex temp; temp.SetRe(left.GetRe() + right.GetRe()); temp.SetIm(left.GetIm() + right.GetIm()); return temp; } Complex operator -(const Complex& left, const Complex& right) { Complex temp; temp.SetRe(left.GetRe() - right.GetRe()); temp.SetIm(left.GetIm() - right.GetIm()); return temp; } Complex operator *(const Complex& left, const Complex& right) { Complex temp; double re=left.GetRe()*right.GetRe() – left.GetIm()*right.GetIm(); double im=left.GetRe()*right.GetRe() + left.GetIm()*right.GetIm(); temp.SetRe(re); temp.SetIm(im); return temp; }
  • 22.
    http://www.slideshare.net/IgorShkulipa 22 Потоки ввода-вывода cinи cout - объекты классов istream (от Input Stream - поток ввода) и ostream (от Output Stream - поток вывода) соответственно. Именно для этих классов перегружены операторы извлечения и вставки (<< и >>). Базовым классом является класс ios (от Input/Output Stream - потоковый ввод/вывод). У класса ios довольно много производных классов. Наследниками istream и ostream являются ifstream (от Input File Stream) и ofstream (от Output File Stream). Которые используются для ввода/вывода в файлы. Кроме того, есть ещё один класс - fstream (от File Stream - фаловый поток), в котором объединены возможности ifstream и ofstream. fstream наследуется одновременно и от istream, и от ostream.
  • 23.
    http://www.slideshare.net/IgorShkulipa 23 Перегрузка операторовввода-вывода class Complex { ... // Расширение класса Complex friend ostream operator <<(ostream& out, Complex& c); friend istream operator >>(istream& in, Complex& c); } ostream &operator<<(ostream &out, Complex &c) { if (c.GetIm()==0) out<<c.GetRe()<<“n”; if (c.GetIm()>0) out<<c.GetRe()<<“+”<<c.GetIm()<<“i”<<“n”; if (c.GetIm()<0) out<<c.GetRe()<<“-”<<-c.GetIm()<<“i”<<“n”; return out; } istream &operator>>(istream &in, Complex &c) { double re, im; in >> re >> im; c.SetRe(re); c.SetIm(im); return in; }
  • 24.
    http://www.slideshare.net/IgorShkulipa 24 Результат #include “complex.h” voidmain() { Complex c1(1,2); Complex c2(2,3); cout << c1; cout << c2; cin >> c1; cin >> c2; cout << c1+c2; cout << c1-c2; cout << c1*c2; }
  • 25.
    http://www.slideshare.net/IgorShkulipa 25 Лабораторная работа№9. Перегрузка операций Создать класс «Вектор» произвольного размера. Реализовать методы: ⚫ Конструкторы и деструктор ⚫ Доступа к данным ⚫ Вычисления модуля вектора Выполнить перегрузку операций: ⚫ Сложения, вычитания, скалярного умножения векторов ⚫ Умножения вектора на скаляр ⚫ Сравнения векторов (==, !=) ⚫ Операции присваивания (=) ⚫ Операцию индексации ( [ ] ) ⚫ Операции ввода-вывода Создать приложение для демонстрации работы класса. Меню приложения реализовать в виде класса, согласно паттерну Singleton.
  • 26.
    http://www.slideshare.net/IgorShkulipa 26 Бонусный слайд№1. Заголовок класса «Вектор» class Vector { private: double* _data; int _size; public: Vector(); Vector(int); Vector(double*, int); Vector(Vector&); ~Vector(); Vector operator = (Vector &v) {//Код оператора} friend bool operator ==(const Vector& left, const Vector& right); friend bool operator !=(const Vector& left, const Vector& right); friend Vector operator +(const Vector& left, const Vector& right); friend Vector operator -(const Vector& left, const Vector& right); friend double operator *(const Vector& left, const Vector& right); friend Vector operator *(const Vector& vect, double scalar); friend ostream operator <<(ostream& out, Vector& v); friend istream operator >>(istream& in, Vector& v); double operator[](int i); int GetSize(); double* GetData(); void SetSize(int); void SetData(double*); }
  • 27.
    http://www.slideshare.net/IgorShkulipa 27 Бонусный слайд№2. Операция индексации double Vector::operator[](int i) { if(i<_size) { return _data[i]; } else { return NULL; } }