***** 1 - Redmine DIHT

advertisement
Объектно-ориентированное
программирование
 Парадигма программирования, основанная на
представлении предметной области в виде
взаимосвязанных абстрактных объектов и их
реализаций
Классы и объекты
 В ООП вводится понятие Класса –
пользовательского типа данных, объединяющего
данные и методы их обработки
 Объектом называется экземпляр класса
 Собака – это класс
 Собака Жучка из 3 подъезда – это объект,
представитель или экземпляр класса «Собака»
Объявление класса в С++
class <Имя класса>
{
// поля класса (данные и методы)
};
Данные объекта (переменные объекта,
члены-данные)
 Члены-данные (data members) хранят всю
необходимую информацию об объекте, формируют
его состояние, характеристики и т.п.
 Изменение состояния объекта или его
характеристик связано с изменением данных, в нем
содержащихся
Методы класса
 Класс может содержать один или более методов,
позволяющих осуществлять манипуляцию
данными объекта
 Метод объекта – программный код, выполненный
в виде процедуры или функции, реагирующий на
передачу объекту определенного сообщения
 Вызов метода объекта может приводить к
изменению его состояния (значение членовданных), а может и не приводить
 Пример 1: поиск и замена текста в документе
 Пример 2: проверка правописания текста документа
Свойства
 Свойство – составляющая часть объекта, доступ к
которой осуществляется программистом как к
переменной объекта
 В некоторых объектно-ориентированных языках
программирования (например, в C++ и Java)
свойства, как элемент языка, отсутствуют
 В этом случае в класс добавляют методы,
посредством которых осуществляется доступ к
необходимым переменным класса
Пример: Треугольник
 Свойства






Координаты вершины A
Координаты вершины B
Координаты вершины C
Площадь
Периметр
Координаты центра вписанной окружности
 Методы
 Переместить в заданном направлении
 Отмасштабировать
 Повернуть вокруг заданной точки
class Point
{
public:
double x, y;
};
class Triangle
{
public:
double GetArea();
double GetPerimeter();
Point GetCenter();
void Move(double dx, double dy);
void Scale(double sx, double sy);
void Rotate(Point center, double angle);
Point p0, p1, p2;
};
Важнейшие принципы ООП
 Абстракция данных
 Инкапсуляция
 Наследование
 Полиморфизм
Абстракция данных
 Объекты представляют неполную информацию о
реальных сущностях предметной области
 Абстракция позволяет оперировать с объектном на
уровне, адекватном решаемой задаче
 Высокоуровневые обращения к объекту могут
обрабатываться с помощью вызова функций и
методов низкого уровня
Инкапсуляция
 Инкапсуляция - способность объекта скрывать
внутреннее устройство своих свойств и методов
 Согласно данному принципу, класс должен
рассматриваться как черный ящик

Внешний пользователь не знает детали реализации
объекта и работает с ним только путем предоставленного
объектом интерфейса
 Следование данному принципу может уменьшить
число связей между классами и упростить их
независимую реализацию, модификацию и
тестирование
Пример. Стек целых чисел
class IntStack
{
public:
void Push(int value);
int Pop();
bool IsEmpty()const;
private:
// здесь располагаются данные
// необходимые для реализации стека целых чисел
};
Наследование
 Наследование позволяет описать новый класс на
основе уже существующего родительского
(базового) класса
 Класс-потомок может добавить свои собственные
свойства и методы, пользоваться методами и
свойствами базового класса
 Наследование позволяет строить иерархии классов
Пример
class Plane
{
public:
void TakeOff();
void Fly();
void Land();
private:
double m_fuel;
};
class MilitaryPlane : public Plane
{
public:
void Attack();
private:
int m_ammo;
};
Полиморфизм
 Полиморфизмом называют явление, при
котором классы-потомки могут изменять
реализацию метода класса-предка, сохраняя его
интерфейс
 Полиморфизм позволяет обрабатывать объекты
классов-потомков как однотипные объекты, не
смотря на то, что реализация методов у них может
различаться
class Shape
{
public:
virtual double GetArea()=0;
};
class Rectangle : public Shape
{
public:
virtual double GetArea()
{
return width * height;
}
private:
double width, height;
};
class Circle : public Shape
{
public:
virtual double GetArea()
{
return 3.1415927 * radius * radius;
}
private:
double radius;
};
Объявление класса в C++
 Для объявления класса в C++ служит ключевое
слово class
 Синтаксис
 class идентификатор
{
// объявление данных и методов
};
 Реализация методов класса может быть вынесена за
пределы объявления класса
Пример
class Date
{
int year, month, day;
void next();
void print();
};
// Реализация методов класса
void Date::print()
{
printf(“%d/%d/%d”, day, month, year);
}
void Date::next()
{
// ...
}
Размещение классов в
различных файлах
 Общепринятой практикой является
размещение объявления классов в
заголовочных файлах .h, а их реализации – в
файлах .cpp
 Повышение модульности проекта
 Каждый класс может быть подключен для
дальнейшего использования при помощи директивы
#include “имя заголовочного файла”
 При внесении изменений в реализацию метода
класса перекомпиляции подвергнутся только
измененные файлы
Пример
date.h
class Date
{
public:
void Next();
void Print();
private:
int m_day;
int m_month;
int m_year;
};
date.cpp
main.cpp
#include “date.h”
#include “date.h”
void Date::Next()
{
// ...
}
int main()
{
Date date1;
return 0;
}
void Date::Print()
{
// ...
}
Ограничение доступа к данным и
методам класса
 Доступ к данным и методам класса извне может
быть ограничен
 Рекомендуется запрещать доступ к данным класса в
обход его методов
 Для разделения прав доступа к полям класса
используются ключевые слова
 public:
 private:
 protected:
Публичные (public) поля класса
 Public-методы и данные класса определяют его
интерфейс
 доступ к ним возможен из любой части кода
 необходимо помещать в public-раздел класса только
необходимый набор методов, выполняющих
высокоуровневые операции над объектом класса
Закрытые (частные) поля класса
 Private-данные и методы класса определяют его
реализацию
 Доступ к ним разрешен только из методов данного
класса
 Рекомендуется все данные класса делать
закрытыми, их обработку осуществлять внутри
методов
 Закрытые методы класса обычно используются
публичными методами, решая внутренние задачи
класса
Защищенные поля класса
 Protected-данные и методы определяют интерфейс
для производных классов
 Доступ к ним разрешен изнутри методов данного
класса и всех его потомков
 В защищенной зоне размещают методы, которые не
должны быть видны снаружи класса, но реализация
которых может быть переопределена или
использована производными классами
Пример
class Date
{
public:
void Next();
void Print();
private:
int year, month, day;
};
// Реализация методов класса
void Date::Print()
{
printf(“%d/%d/%d”, day, month, year);
}
void Date::Next()
{
// ...
}
Ссылка на себя
 Внутри методов класса для обращения к данным
класса можно использовать их имена
 В метод класса неявно передается указатель на
объект, для которого он вызывается
 Доступен данный указатель по ключевому слову this
Пример
class ListItem
{
public:
void Append(ListItem *pItem)
{
pItem->m_pNext = this;
m_pPrevious = pItem;
m_pNext = NULL;
}
private:
ListItem *m_pNext;
ListItem *m_pPrevious;
int
m_data;
};
Константные методы
 В языке C++ методы объекта, не изменяющие его
состояния (его данных) могут быть объявлены
константными
 Например, методы, возвращающие значения
определенных полей данных
 Изменить данные класса из константного метода
нельзя
Когда возникает необходимость
в константных методах
 Если объект был объявлен как константа, либо
доступен по константной ссылке или указателю на
const, то вызвать у него можно только константные
методы
 Это заставляет объявлять методы константными
везде, где это только возможно
Пример
class IntArray
{
public:
…
int GetSize()const
{
return m_numberOfItems;
}
void ClearElements()
{
delete [] m_pData;
m_pData = NULL;
m_numberOfItems = 0;
}
private:
int *m_pData;
int m_numberOfItems;
};
void f(IntArray const& array)
{
int i = array.GetSize();
array.ClearElements();
}
// можно
// нельзя – неконстантные методы недоступны
Изменчивые (mutable) данные
класса
 Данные класса, которые все-таки нужно изменять
из константных методов класса в С++ объявляются
с ключевым словом mutable
 Пользоваться этой возможностью следует аккуратно,
четко осознавая, что даже в этом случае константные
методы не должны изменять состояние объекта

Под состоянием объекта здесь понимается информация о
нем, доступная посредством публичных методов
Пример
class VeryComplexShape
{
public:
VeryComplexShape()
{
m_areaInitialized = false;
}
double GetArea()const
{
if (!m_areaInitialized)
{
// вычисляем площадь фигуры (задача требует длительных вычислений)
m_areaInitialized = true;
…
}
return m_area;
}
void ModifyShape(...)
{
m_areaInitialized = false;
// ...
}
private:
mutable bool m_areaInitialized;
mutable double m_area;
};
Инициализация экземпляра
класса
 Для инициализации состояния объекта в
момент его создания существует специальная
функция – конструктор
 Конструктор имеет то же имя, что и имя класса
 Тип возвращаемого значения для конструктора не
указывается (даже void)
 Конструктор вызывается в момент создания
экземпляра класса (объявление переменной класса
или вызов оператора new)
 Класс может иметь несколько конструкторов,
предоставляющих различные способы
инициализации объекта
Пример
class Date
{
public:
Date(int day, int month)
{
m_day = day;
m_month = month;
m_year = GetCurrentYear();
}
Date(int day, int month, int year)
{
m_day = day;
m_month = month;
m_year = year;
}
private:
int m_day, m_month, m_year;
};
Конструктор по умолчанию
 Конструктор, не имеющий параметров, называется
конструктором по умолчанию
 Поля данных в таком конструкторе
инициализируются значениями по умолчанию
 Создавать такой конструктор или не создавать –
зависит от конкретной задачи
Инициализация данных
экземпляра класса
 В качестве данных класса могут выступать другие
классы
 Их инициализация осуществляется ДО выполнения
тела конструктора
 Для их инициализации вызываются конструкторы
по умолчанию

Если таковых не имеется, программист должен
использовать списки инициализации
Списки инициализации
 Применяются для инициализации полей класса в
конструкторе ДО выполнения его тела
 Использование списков инициализации –
единственное решение в случае, когда класс
содержит внутри себя



поля, являющиеся классами без конструкторов по
умолчанию
константы
ссылки
Пример
class Foo
{
public:
Foo(int i, int j = 0)
:m_i(i)
,m_j(j)
{
}
private:
int m_i, m_j;
};
class Bar
{
public:
Bar() :m_foo(3, 5)
{
}
Bar(int i, int j) :m_foo(i, j)
{
}
private:
Foo m_foo;
};
Деинициализация экземпляра
класса
 В ходе своей работы объект может
использовать определенные системные
ресурсы
 Динамическая память, открытые файлы, сетевые
соединения и т.п.
 Для освобождения этих ресурсов служит
особый метод класса – деструктор
 Имя деструктора совпадает с именем класса, только
перед ним указывается символ ~ (тильда)
 Данная функция вызывается автоматически при
уничтожении экземпляра класса:


Выход за пределы блока, в котором объявлен экземпляр
класса
Вызов оператора delete или delete []
Пример
class MyFile
{
public:
MyFile():m_pFile(NULL)
{}
~MyFile()
{
Close();
}
bool Open(const char *fileName)
{
Close();
m_pFile = fopen(fileName, “r”);
return m_pFile != NULL;
}
void Close()
{
if (m_pFile){fclose(m_pFile); m_pFile = NULL;}
}
private:
FILE *m_pFile;
};
Конструктор копирования
(копирующий конструктор)
 В языке C++ существует специальный тип
конструкторов, использующийся для создания
копии объекта
 Явное создание копии объекта программистом
 Неявное создание копии объекта



Возврат объекта из функции
Передача объекта в функцию по значению
Во время работы механизма исключений
 Синтаксис
 Type(Type const& t);
Автоматически сгенерированный
конструктор копирования
 Если программист не определит конструктор
копирования явно, компилятор сгенерирует его во
время компиляции
 Автоматически сгенерированный конструктор
копирования осуществляет копирование всех
полей класса, вызывая для них их конструкторы
копирования
#include "stdio.h"
class Foo
{
public:
Foo():m_moo(0)
{
}
Foo(Foo const& foo) :m_moo(foo.m_moo)
{
printf("Creating copy of foo\n");
}
private:
int
m_moo;
};
class Bar
{
public:
void Do()
{
printf("Do\n");
}
private:
Foo m_foo;
};
void f(Bar b)
{
printf("f()\n");
b.Do();
}
Bar g()
{
printf("g()\n");
Bar b;
return b;
}
int main()
{
Bar b0;
printf("Call f()\n");
f(b0);
printf("Call g()\n");
Bar b1 = (g());
b1.Do();
return 0;
}
OUTPUT:
Call f()
Creating copy of foo
f()
Do
Call g()
g()
Creating copy of foo
Do
Создание собственного
конструктора копирования
 Часто возникают ситуации, когда
автоматически сгенерированный конструктор
копирования не подходит
 Пример – класс, реализующий массив
 Стандартный конструктор копирования просто скопирует
значение указателя на элементы массива, в то время как
необходимо выделить в динамической памяти новый
массив и скопировать в него данные из оригинального
массива
 В этом случае программист должен разработать
собственный конструктор копирования
Пример
#include "stdio.h"
#include "memory.h"
class IntArray
{
public:
IntArray():m_pData(NULL), m_size(0){}
IntArray(IntArray const& arr)
:m_pData(new int [arr.m_size])
,m_size(arr.m_size)
{
if (m_size != 0)
{
memcpy(m_pData, arr.m_pData, sizeof(int) * m_size);
}
}
private:
int * m_pData;
int m_size;
};
Запрещение копирования
объектов
 Возможны ситуации, когда операция
копирования объекта не имеет смысла и
должна быть запрещена
 Класс, инкапсулирующий сетевое соединение
 Класс, инкапсулирующий работу с файлом
 Объект должен существовать в единственном
экземпляре внутри приложения, например,
«клавиатура»
 Для запрещения копирования объекта,
конструктор копирования объявляется в закрытой
(private) области класса
 Реализацию данного конструктора можно не писать
Пример
class CFile
{
public:
// …
private:
CFile(Cfile const&);
// …
};
Download