Слайд 2Объектно-ориентированное программирование
Парадигма программирования, основанная на представлении предметной области в виде
взаимосвязанных абстрактных объектов и их реализаций
Основными концепциями ООП являются Классы
и Объекты
Слайд 3Классы и объекты
В ООП вводится понятие Класса – пользовательского типа
данных, объединяющего данные и методы их обработки
Класс – тип, описывающий
устройство объекта
Объектом называется экземпляр класса
Собака – это класс
Собака Жучка из 3 подъезда – это объект, представитель или экземпляр класса «Собака»
Чертеж дома – класс, построенный по нему дом - объект
Слайд 4Объявление класса в С++
class
{
// поля класса (данные и
методы)
};
Слайд 5Данные объекта (переменные объекта, члены-данные)
Члены-данные (data members) хранят всю необходимую
информацию об объекте, формируют его состояние, характеристики и т.п.
Изменение состояния
объекта или его характеристик связано с изменением данных, в нем содержащихся
Слайд 6Пример
Данными класса «Автомобиль» могут являться
Марка
Год выпуска
Регистрационный номер
Количество топлива в баке
Величина
пробега
Цвет кузова
Слайд 7Методы класса
Класс может содержать один или более методов, позволяющих осуществлять
манипуляцию данными объекта
Метод объекта – программный код, выполненный в виде
процедуры или функции, реагирующий на передачу объекту определенного сообщения
Вызов метода объекта может приводить к изменению его состояния (значение членов-данных), а может и не приводить
Пример 1: поиск и замена текста в документе
Пример 2: проверка правописания текста документа
Слайд 8Пример
Методами класса «Автомобиль» будут являться:
Проехать N километров
Увеличивает пробег, уменьшает топливо
Перекрасить
кузов
Изменяет цвет кузова
Заправить топливо
Увеличивает количество топлива
Слайд 9Свойства
Свойство – составляющая часть объекта, доступ к которой осуществляется программистом
как к переменной объекта
В некоторых объектно-ориентированных языках программирования (например, в
C++ и Java) свойства, как элемент языка, отсутствуют
В этом случае в класс добавляют методы, посредством которых осуществляется доступ к необходимым переменным класса
Слайд 10Пример: Треугольник
Свойства
Координаты вершины A
Координаты вершины B
Координаты вершины C
Площадь
Периметр
Координаты центра вписанной
окружности
Методы
Переместить в заданном направлении
Отмасштабировать
Повернуть вокруг заданной точки
Слайд 11class 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);
private:
Point
p0, p1, p2;
};
Слайд 12Важнейшие принципы ООП
Абстракция данных
Инкапсуляция
Наследование
Полиморфизм
Слайд 13Абстракция данных
Объекты представляют неполную информацию о реальных сущностях предметной области
Абстракция
позволяет оперировать с объектном на уровне, адекватном решаемой задаче
Высокоуровневые обращения
к объекту могут обрабатываться с помощью вызова функций и методов низкого уровня
Слайд 14Инкапсуляция
Инкапсуляция - способность объекта скрывать внутреннее устройство своих свойств и
методов
Согласно данному принципу, класс должен рассматриваться как черный ящик
Внешний пользователь
не знает детали реализации объекта и работает с ним только путем предоставленного объектом интерфейса
Следование данному принципу может уменьшить число связей между классами и упростить их независимую реализацию, модификацию и тестирование
Слайд 15class IntStack
{
public:
void Push(int value);
int Pop();
bool IsEmpty()const;
private:
// здесь располагаются данные
// необходимые
для реализации стека целых чисел
};
Пример. Стек целых чисел
Слайд 16Наследование
Наследование позволяет описать новый класс на основе уже существующего родительского
(базового) класса
Класс-потомок может добавить свои собственные свойства и методы, пользоваться
методами и свойствами базового класса
Наследование позволяет строить иерархии классов
Слайд 17Пример
class Plane
{
public:
void TakeOff();
void Fly();
void Land();
private:
double m_fuel;
};
class MilitaryPlane : public Plane
{
public:
void
Attack();
private:
int m_ammo;
};
Слайд 18Полиморфизм
Полиморфизмом называют явление, при котором классы-потомки могут изменять реализацию метода
класса-предка, сохраняя его интерфейс
Полиморфизм позволяет обрабатывать объекты классов-потомков как однотипные
объекты, не смотря на то, что реализация методов у них может различаться
Слайд 19class 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;
};
Слайд 20Объявление класса в C++
Для объявления класса в C++ служит ключевое
слово class
Синтаксис
class идентификатор
{
// объявление данных и методов
};
Реализация методов
класса может быть вынесена за пределы объявления класса
Слайд 21Пример
class Date
{
public:
void Next();
void Print()const;
private:
int year, month, day;
};
// Реализация методов класса
void
Date::Print()const
{
std::cout
year << "\n";
}
void Date::Next()
{
// ...
}
Слайд 22Размещение классов в различных файлах
Общепринятой практикой является размещение объявления классов
в заголовочных файлах .h, а их реализации – в файлах
.cpp
Повышение модульности проекта
Каждый класс может быть подключен для дальнейшего использования при помощи директивы #include “имя заголовочного файла”
При внесении изменений в реализацию метода класса перекомпиляции подвергнутся только измененные файлы
Слайд 23Пример
date.h
class Date
{
public:
void Next();
void Print()const;
...
private:
int m_day;
int m_month;
int m_year;
};
date.cpp
#include “date.h”
void Date::Next()
{
// ...
}
void
Date::Print()const
{
// ...
}
...
main.cpp
#include “date.h”
int main()
{
Date date1;
...
return 0;
}
Слайд 24Ограничение доступа к данным и методам класса
Доступ к данным и
методам класса извне может быть ограничен
Рекомендуется запрещать доступ к данным
класса в обход его методов
Для разделения прав доступа к полям класса используются ключевые слова
public:
private:
protected:
Слайд 25Публичные (public) поля класса
Public-методы и данные класса определяют его интерфейс
доступ
к ним возможен из любой части кода
необходимо помещать в public-раздел
класса только необходимый набор методов, выполняющих высокоуровневые операции над объектом класса
Слайд 26Закрытые (частные) поля класса
Private-данные и методы класса определяют его реализацию
Доступ
к ним разрешен только из методов данного класса
Рекомендуется все данные
класса делать закрытыми, их обработку осуществлять внутри методов
Закрытые методы класса обычно используются публичными и защищенными методами, решая внутренние задачи класса
Слайд 27Защищенные поля класса
Protected-данные и методы определяют интерфейс для производных классов
Доступ
к ним разрешен изнутри методов данного класса и всех его
потомков
В защищенной зоне размещают методы, которые не должны быть видны снаружи класса, но реализация которых может быть переопределена или использована производными классами
Слайд 28Уровни доступа к полям и методам класса
Слайд 29Пример
// Просто "человек"
class CPerson
{
public:
// Потратить заданную сумму денег
bool SpendMoney(int amount)
{
//
Нельзя потратить отрицательное количество денег или больше,
// чем имеется в
наличии
if (amount < 0 || amount > GetMoneyAmount())
{
return false;
}
SpendMoneyImpl(amount);
return true;
}
// Фактическая реализация метода получения количества денег
// будет переопределена в классах-наследниках
virtual int GetMoneyAmount()const = 0;
protected:
// Фактическая реализация метода траты денег будет переопределена
// в классах-наследниках
virtual void SpendMoneyImpl(int amount) = 0;
}
Слайд 30Школьник
// Школьник
class CPupil : public CPerson
{
public:
CPupil():m_moneyForSweets(100){}
virtual int GetMoneyAmount()const
{
// Из всех
денег - только деньги на сладости
return m_moneyForSweets;
}
protected:
virtual void SpendMoneyImpl(int amount)
{
//
Эти же деньги он и может потратить
assert(amount <= m_moneyForSweets);
m_moneyForSweets -= amount;
}
private:
int m_moneyForSweets;
};
Слайд 31Студент
class CStudent : public CPerson
{
public:
CStudent():m_moneyForBeer(100),m_moneyForFood(100),m_moneyForGirls(100){}
virtual int GetMoneyAmount()const
{ // У студента деньги
формируются из нескольких "заначек"
return m_moneyForBeer + m_moneyForFood + m_moneyForGirls;
}
protected:
virtual void
SpendMoneyImpl(int amount)
{ // сперва тратим деньги, отложенные на пиво
const int beerMoney = std::min(amount, m_moneyForBeer);
m_moneyForBeer -= beerMoney; amount -= beerMoney;
// затем придется тратить деньги, отложенные на девушку
const int girlMoney = std::min(amount, m_moneyForGirls);
m_moneyForGirls -= girlMoney; amount -= girlMoney;
// остаток будем тратить из запасов на еду
assert(amount <= m_moneyForFood);
m_moneyForFood -= amount;
}
private:
int m_moneyForBeer, m_moneyForGirls, m_moneyForFood;
};
Слайд 32Аспирант
class CAdvancedStudent : public CStudent
{
public:
CAdvancedStudent():m_moneyForThesis(100){}
virtual int GetMoneyAmount()const
{ // Денег столько же,
сколько есть у студента + деньги на защиту диссертации
return CStudent::GetMoneyAmount()
+ m_moneyForThesis;
}
protected:
virtual void SpendMoneyImpl(int amount)
{
int studentMoney = CStudent::GetMoneyAmount();
if (studentMoney < amount)
{
// придется взять часть денег из диссертации
int thesisMoney = amount - studentMoney;
m_moneyForThesis -= thesisMoney; amount -= thesisMoney;
}
// Остальное тратим, как потратил бы студент
CStudent::SpendMoneyImpl(amount);
}
private:
int m_moneyForThesis;
};
Слайд 33Ссылка на себя
Внутри методов класса для обращения к данным класса
можно использовать их имена
В метод класса неявно передается указатель на
объект, для которого он вызывается
Доступен данный указатель по ключевому слову this
Слайд 34Пример
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;
};
Слайд 35Константные методы
В языке C++ методы объекта, не изменяющие его состояния
(его данных) могут быть объявлены константными
Например, методы, возвращающие значения определенных
полей данных
Изменить данные класса из константного метода нельзя
Слайд 36Когда возникает необходимость в константных методах
Если объект был объявлен как
константа, либо доступен по константной ссылке или указателю на const,
то вызвать у него можно только константные методы
Это заставляет объявлять методы константными везде, где это только возможно
Слайд 37Пример
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(); //
нельзя – неконстантные методы недоступны
}
Слайд 38Изменчивые (mutable) данные класса
Данные класса, которые все-таки нужно изменять из
константных методов класса в С++ объявляются с ключевым словом mutable
Пользоваться
этой возможностью следует аккуратно, четко осознавая, что даже в этом случае константные методы не должны изменять состояние объекта
Под состоянием объекта здесь понимается информация о нем, доступная посредством публичных методов
Слайд 39Пример
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;
};
Слайд 40Инициализация экземпляра класса
Для инициализации состояния объекта в момент его создания
существует специальная функция – конструктор
Конструктор имеет то же имя, что
и имя класса
Тип возвращаемого значения для конструктора не указывается (даже void)
Конструктор вызывается один в момент создания экземпляра класса (объявление переменной класса или вызов оператора new)
Класс может иметь несколько конструкторов, предоставляющих различные способы инициализации объекта
Для инициализации объекта может быть вызван только один из них
Слайд 41Пример
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;
};
Слайд 42Конструктор по умолчанию
Конструктор, не имеющий параметров, называется конструктором по умолчанию
Поля
данных в таком конструкторе инициализируются значениями по умолчанию
Создавать такой конструктор
или не создавать – зависит от конкретной задачи
Слайд 43Инициализация данных экземпляра класса
В качестве данных класса могут выступать другие
классы
Их инициализация осуществляется ДО выполнения тела конструктора
Для их инициализации вызываются
конструкторы по умолчанию
Если таковых не имеется, программист должен использовать списки инициализации
Слайд 44Списки инициализации
Применяются для инициализации полей класса в конструкторе ДО выполнения
его тела
Использование списков инициализации – единственное решение в случае, когда
класс содержит внутри себя
поля, являющиеся классами без конструкторов по умолчанию
константы
ссылки
Слайд 45Пример
class CDocument
{
public:
void Open(string const& fileName);
void Save(string const& fileName)const;
// Прочие операции
на документом
};
class CEditor
{
public:
CEditor(CDocument & document)
:m_document(document)
{
}
void Undo();
void Redo();
void ReplaceText(
size_t offset, size_t
length,
string const& text);
// Прочие операции редактора
// …
private:
CDocument & m_document;
};
int main(int argc, char * argv[])
{
CDocument document;
document.Open("fileName.doc");
CEditor editor(document);
// Вызываем операции редактора документа
// …
document.Save("fileName1.doc");
return 0;
}
Слайд 46Деинициализация экземпляра класса
В ходе своей работы объект может использовать определенные
системные ресурсы
Динамическая память, открытые файлы, сетевые соединения и т.п.
При разрушении
объекта используемые им единолично ресурсы должны освобождаться
В C++ для освобождения этих ресурсов служит особый метод класса – деструктор
Слайд 47Деструктор
Имя деструктора совпадает с именем класса, только перед ним указывается
символ ~ (тильда)
Данный метод вызывается автоматически при уничтожении экземпляра класса:
Выход
за пределы блока, в котором объявлен экземпляр класса
Вызов оператора delete или delete []
Слайд 48Содержимое тела деструктора
В деструкторе программист размещает код, выполняющий действия, завершающие
жизненный цикл объекта
Освобождение выделенных объектом ресурсов
Что-нибудь еще
Слайд 49Пример
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;
};
int main(int argc, char *
argv[])
{
MyFile file;
if (file.Open("text.txt"))
{
// Выполняем операции над файлом
}
MyFile * pFile = new MyFile();
...
// вызов деструктора и освобождение
// памяти, занимаемой объектом pFile
// в куче
delete pFile;
pFile = NULL;
return 0;
} // При выходе из данного блока
// будет вызван деструктор объекта file
Слайд 50Жизнь после смерти
В C++ после выполнения тела деструктора происходит автоматический
вызов деструкторов членов-данных класса
Порядок вызова деструкторов обратен порядку объявления
Если класс
был унаследован от другого класса, будет вызван деструктор родительского класса
И т.д. по цепочке
После этого происходит освобождение занимаемой объектом памяти
Слайд 51Класс «Колесо»
class Wheel
{
public:
Wheel(string const& name)
:m_name(name)
{
cout
m_name
"\n";
}
private:
string m_name;
};
Слайд 52Класс «Автомобиль»
class Car
{
public:
Car(string const& name):m_name(name)
,m_frontRight("Front right“),m_rearLeft("Rear left")
,m_rearRight("Rear right“),m_frontLeft("Front left")
{
cout
"Constructing car "
<< "Destroying car " << m_name << "\n";
}
private:
string m_name;
Wheel m_frontLeft;
Wheel m_frontRight;
Wheel m_rearLeft;
Wheel m_rearRight;
};
Слайд 53Класс «Супер автомобиль»
class SuperCar : public Car
{
public:
SuperCar(string const& name)
:Car(name)
,m_fifthWheel("Fifth wheel")
{
cout
"Destroying super car " << GetName() << "\n";
}
private:
Wheel m_fifthWheel;
};
Слайд 54Порядок вызова конструкторов и деструкторов
int main(int argc, char* argv[])
{
SuperCar ladaKalina("Lada
Kalina");
return 0;
}
Constructing wheel Front left
Constructing wheel Front right
Constructing wheel Rear
left
Constructing wheel Rear right
Constructing car Lada Kalina
Constructing wheel Fifth wheel
Constructing super car Lada Kalina
Destroying super car Lada Kalina
Destroying wheel Fifth wheel
Destroying car Lada Kalina
Destroying wheel Rear right
Destroying wheel Rear left
Destroying wheel Front right
Destroying wheel Front Left
Конструирование полей базового класса Car
Выполнение тела конструктора класса Car
Конструирование полей класса SuperCar
Тело конструктора класса SuperCar
Тело деструктора класса SuperCar
Деструкторы полей полей класса SuperCar
Тело деструктора класса Car
Разрушение полей класса SuperCar
Output:
Слайд 55Автоматически сгенерированный деструктор
Создается компилятором, если в классе не был явно
объявлен деструктор
Автоматически сгенерированный конструктор имеет пустое тело
Остальные механизмы разрушения объекта
работают обычным образом
Деструктор
Разрушение указателя не выполняет освобождения памяти, на который он ссылается
Слайд 56Корректно работающий автоматически сгенерированный деструктор
class CDataBase
{
public:
private:
std::string m_databaseName;
MyFile m_databaseFile;
};
При разрушении экземпляра
класса CDataBase деструкторы содержащихся в нем объектов освободят занимаемую память
Слайд 57Некорректно работающий автоматически сгенерированный деструктор
class CDataBase
{
public:
void DoSomething()
{
m_bufferArray = new int[100];
}
private:
int
* m_bufferArray;
std::string m_databaseName;
MyFile m_databaseFile;
};
Автоматически сгенерированный деструктор не выполнит удаление массива
m_bufferArray
Слайд 59Конструктор копирования (копирующий конструктор)
В языке C++ существует специальный тип конструкторов,
использующийся для создания копии объекта
Явное создание копии объекта программистом
Неявное создание
копии объекта
Возврат объекта из функции
Передача объекта в функцию по значению
Во время работы механизма исключений
Синтаксис
Type(Type const& t);
Слайд 60Автоматически сгенерированный конструктор копирования
Если программист не определит конструктор копирования явно,
компилятор сгенерирует его во время компиляции
Автоматически сгенерированный конструктор копирования осуществляет
копирование всех полей класса, вызывая для них их конструкторы копирования
Слайд 61#include "stdio.h"
class Foo
{
public:
Foo():m_moo(0)
{
}
Foo(Foo const& foo) :m_moo(foo.m_moo)
{
std::cout
foo\n";
}
private:
int m_moo;
};
class Bar
{
public:
void Do()
{
std::cout
<< "g()\n";
Bar b;
return b;
}
int main()
{
Bar b0;
std::cout << "Call f()\n";
f(b0);
std::cout << "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
Слайд 62Создание собственного конструктора копирования
Часто возникают ситуации, когда автоматически сгенерированный конструктор
копирования не подходит
Пример – класс, реализующий массив
Стандартный конструктор копирования просто
скопирует значение указателя на элементы массива, в то время как необходимо выделить в динамической памяти новый массив и скопировать в него данные из оригинального массива
В этом случае программист должен разработать собственный конструктор копирования
Слайд 63Пример
#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;
};
Слайд 64Запрещение копирования объектов
Возможны ситуации, когда операция копирования объекта не имеет
смысла и должна быть запрещена
Класс, инкапсулирующий сетевое соединение
Класс, инкапсулирующий работу
с файлом
Объект должен существовать в единственном экземпляре внутри приложения, например, «клавиатура»
Для запрещения копирования объекта, конструктор копирования объявляется в закрытой (private) области класса
Реализацию данного конструктора можно не писать
Слайд 65Пример
class CFile
{
public:
// …
private:
CFile(CFile const&);
// …
};
Слайд 66Статические данные и методы класса
Слайд 67Для чего нужны статические данные класса
Класс – это тип данных
Для
каждого объекта создается своя собственная копия членов данных
Методы класса работают
с одним из экземпляров класса, на которые ссылается this
Для некоторых классов естественными могли бы оказаться данные, общие для всех экземпляров данного класса
Например, строковое представление имени класса
Константы, общие для всех экземпляров класса, область видимости которых должна быть ограничена методами класса
Такие поля и методы называют статическими и объявляют при помощи ключевого слова static
Слайд 68Особенности
Статические методы не получают указатель this
Статические методы могут обращаться только
к статическим данным класса
Статические методы могут вызывать только статические методы
Либо
нестатические, если им передается указатель или ссылка на объект класса
Статические методы имеют доступ к закрытым и защищенным полям и методам класса, через экземпляры классов
Доступ к статическим методам и данным класса осуществляется по имени класса (создавать экземпляр не требуется)
Слайд 69Пример
Foo.h:
class Foo
{
public:
static std::string const GetClassName()
{
return m_className;
}
private:
static std::string const m_className;
};
Foo.cpp:
#include "Foo.h"
std::string
const Foo::m_className = "Foo";
main.cpp:
#include "foo.h"
int main(int argc, char *argv[])
{
std::cout
Foo::GetClassName() << "\n";
return 0;
}
Слайд 70Область применения
Запрещение создания объектов в области стека
Приватный конструктор + статический
метод для создания класса в куче
Паттерн «одиночка» (singleton)
Объект с глобальным
доступом, существующий в программе в единственном экземпляре
Методы и данные, характерные для класса в целом, а не для отдельных его экземпляров
Создание классов-утилит
Классы, состоящие исключительно из статических методов
Слайд 71Пример – паттерн «Одиночка»
// singleton.h
class Singleton
{
public:
static Singleton & GetInstance()
{
if (!m_pInstance)
{
m_pInstance
= new Singleton();
}
return *m_pInstance;
}
void SomeMethod(){}
private:
Singleton(){}
static Singleton *m_pInstance;
};
// singleton.cpp
Singleton* Singleton::m_pInstance =
NULL;
int main(int argc, char * argv[])
{
Singleton& singleton = Singleton::GetInstance();
singleton.SomeMethod();
return 0;
}
Слайд 72Пример – создание объектов только в динамической памяти
class CFile
{
public:
static boost::shared_ptr
Open(std::string const& fileName)
{
boost::shared_ptr pFile(new CFile());
if (!pFile->OpenFile(fileName))
{
pFile.reset();
}
return pFile;
}
static boost::shared_ptr Create(std::string const&
fileName)
{
boost::shared_ptr
pFile(new CFile());
if (!pFile->CreateFile(fileName))
{
pFile.reset();
}
return pFile;
}
private:
CFile(){...}
bool OpenFile(
string const& fileName){...}
bool CreateFile(
string const& fileName){...}
};
int main(int argc, char * argv[])
{
boost::shared_ptr pFile =
CFile::Open("filename.txt");
if (pFile)
{
...
}
return 0;
}
Слайд 74Вложенное объявление классов и других типов данных
Язык C++ позволяет разместить
объявление одного класса (или другого типа данных) внутри объявления другого
Это
полезно, когда вложенный тип данных используется лишь внешним классом, или совместно с ним
Пример - итераторы стандартных контейнеров STL
Использование вложенного класса
Из методов внешнего класса – по имени вложенного класса
Снаружи – при помощи указания имени внешнего класса:
ExternalClass::Internal
Слайд 75Пример 1
class External
{
public:
class Internal
{
public:
void Foo(){}
};
private:
void Bar()
{
// из методов внешнего
класса можем обращаться по имени
Internal internal;
internal.Foo();
}
};
int main(int argc, char* argv[])
{
External::Internal
internal;
internal.Foo();
return 0;
}
Слайд 76Пример 2 – идиома «Pimpl»
// ComplexClass.h
class CComplexClass
{
public:
CComplexClass();
void Foo();
~CComplexClass();
private:
//
класс Impl хранит все
// приватные данные и методы
// класса CComplexClass
class
Impl;
Impl * m_pImpl;
};
// ComplexClass.cpp
class CComplexClass::Impl
{
public:
void Foo()
{
//…
}
private:
// …
};
CComplexClass::CComplexClass()
:m_pImpl(new Impl())
{
}
CComplexClass::~CComplexClass()
{
delete m_pImpl;
}
void CComplexClass::Foo()
{
m_pImpl->Foo();
}
Данный подход позволяет в языке C++ сократить количество зависимостей времени компиляции от модификации приватной части класса, т.к. она фактически вынесена из заголовочного файла в .CPP файл