- •1. Абстракция и декомпозиция. Основные виды декомпозиции программ.
- •Модульная декомпозиция
- •В заголовочный файл не следует помещать элементы реализации модуля, в том числе и внутренние функции, которые необходимы для реализации, однако не существенны для клиентского когда модуля.
- •Объектная декомпозиция
- •2. Понятие класса и объекта. Переменные-члены и функции-члены. Обращение к членам класса через объект. Указатель this. Константные функции-члены.
- •3. Спецификаторы доступа. Понятие инкапсуляции. Отличие конструкций class и struct. Методы доступа.
- •4. Конструкторы классов, синтаксис, разновидности, моменты вызова конструкторов. Роль конструкторов в соблюдении инвариантов классов.
- •5. Конструкторы по умолчанию (default constructors). Тривиальные и нетривиальные сгенерированные конструкторы классов. Конструирование массивов объектов.
- •6. Списки инициализации. Синтаксис, отличие от присвоений в теле конструктора, необходимость в существовании.
- •7. Деструкторы классов, синтаксис, цель, моменты вызова деструкторов.
- •8. Моменты копирования объектов. Поведение по умолчанию. Конструктор копий и оператор копирующего присвоения.
- •9. Временные объекты. Явные и неявные конструкторы. Оптимизации rvo/nrvo. Временные объекты
- •Неявные и явные конструкторы
- •Запрещение копирования
- •Оптимизация копирования
- •10. Основные отличия между классами-значениями и классами-сущностями. Запрещение копирования объектов. Основные отличия между классами-значениями и классами-сущностями.
- •11. Перемещение объектов. Конструктор перемещения и оператор перемещающего присвоения. Понятие rvalue-ссылки. Функция std::move.
- •12. Перегрузка операторов. Оправданное и неоправданное использование. Пример перегрузки простейшего оператора. Операторы, которые нельзя перегружать.
- •13. Внутриклассовые и глобальные перегруженные операторы. Перегрузка операторов сдвига. Применение перегрузки сдвига для взаимодействия с потоками ввода/вывода.
- •14. Перегрузка операторов сравнения и арифметических операторов. Основные правила реализации и применения.
- •15. Перегрузка операторов индексной выборки, префиксного и постфиксного инкремента/декремента. Перегрузка операторов преобразования типа.
- •16. Статические переменные-члены. Цель применения. Синтаксис. Особенности компоновки.
- •17. Статические функции-члены. Синтаксис, особенности применения. Фабричный метод. Статические функции-члены
- •Фабричный метод
- •19. Физическое и логическое постоянство объектов. Модификатор mutable.
- •20. Класс std::string из стандартной библиотеки. Основная функциональность, способы применения. Особенности внутренней структуры.
- •21. Композиция объектов. Иерархии целое-часть. Структура простейшей композиции по значению в памяти. Ответственность за уничтожение объектов при композиции.
- •22. Ссылочная композиция. Разрываемая композиция. Кратность композиции. Одиночная, множественная и недетерминированная кратность.
- •23. Применение контейнера std::vector для композиции с недетерминированной кратностью. Композиция объектов-значений и объектов-сущностей.
- •24. Композиция объектов с кратностью многие-ко-многим. Основные особенности объектных отношений, способы реализации.
- •25. Наследование классов. Необходимость в отношении наследования. Структура наследования в памяти. Повышающее преобразование типа.
- •26. Критерии оценки корректности применения наследования. Примеры корректного и некорректного применения наследования.
- •27. Конструкторы и деструкторы при наследовании. Моменты и порядок вызовов конструкторов. Передача аргументов конструкторам базового класса.
- •28. Спецификатор доступа protected. Защищенные конструкторы и методы.
- •29. Понижающее преобразование типа (downcast). Опасности. Поля идентификации типов.
- •30. Виртуальные функции. Полиморфизм. Цель. Синтаксис, примеры использования.
- •31. Реализация виртуальных функций. Указатель vptr и таблица vtable. Вызов виртуальной функции. Инициализация служебных данных для работы виртуальных функций в конструкторах.
- •32. Контроль переопределения виртуальных функций. Требования к сигнатурам. Ключевые слова override и final. Ковариантность возвращаемых типов.
- •33. Чисто виртуальные функции и абстрактные классы. Вызов чисто виртуальной функции в конструкторе до завершения инициализации объекта.
- •34. Понятие интерфейса. Применение интерфейсов.
- •35. Множественное наследование конкретных классов. Синтаксис, структура в памяти, особенности применения и реализации.
- •36. Преобразование типов при множественном наследовании в верхнем и нижнем направлениях. Коррекция указателя this.
- •37. Множественное наследование классов с повторяющимся базовым. Синтаксис, структура в памяти, особенности применения и реализации.
- •38. Виртуальные базовые классы. Синтаксис, структура в памяти, особенности применения и реализации. Понятие “самого производного” класса и его роль в организации работы виртуальных базовых классов.
- •39. Механизм rtti - назначение, особенности применения. Структура std::type_info, оператор typeid для выражений и типов.
- •40. Применение оператора dynamic_cast для указателей и ссылок. Основные цели использования. Отличия от операторов static_cast, reinterpret_cast и const_cast.
- •41. Альтернативные решения, заменяющие dynamic_cast. Виртуальные функции для понижающего преобразования. Типовое решение Visitor.
- •42. Обработка исключений. Цели, синтаксис выброса и обработчиков. Выбор обработчика по типу. Передача данных исключения по значению, указателю и ссылке. Исключения языка и стандартной библиотеки.
- •44. Шаблоны функций и классов. Синтаксис определения шаблонов. Инстанцирование шаблонов. Модель включения и явное инстанцирование.
- •Шаблоны классов
- •45. Аргументы шаблонов - типы, константы, шаблонные аргументы шаблонов. Дедукция фактических аргументов шаблонов.
- •46. Понятие обобщенной концепции. Статический полиморфизм по сравнению с динамическим полиморфизмом.
- •Статический полиморфизм
- •47. Итераторы stl - основные разновидности, итераторы контейнеров, итераторы, не связанные с контейнерами.
- •48. Классификация алгоритмов стандартной библиотеки. Примеры применения наиболее часто используемых алгоритмов.
- •49. Функциональные объекты stl. Простые функциональные объекты. Стандартные функциональные объекты. Связыватели std::bind.
- •50. Понятие лямбда-выражения. Синтаксис, особенности использования. Реализация лямбда-выражений компилятором. Список захвата лямбда-выражения.
- •51. Специализация шаблонов. Полная и частичная специализация. Статический выбор вариантов на основе специализации шаблонов.
- •52. Необычный рекуррентный шаблон. Структура, варианты применения.
Фабричный метод
Например, описанный выше класс Student не должен считать корректными значения оценки за пределами интервала [1;100]. Объект, нарушающие данное инвариантное условие существовать не должен, поскольку не соблюдается важное ограничение из рассматриваемой предметной области.
Решить описанную проблему можно применением статического МЕТОДА-ФАБРИКИ (Factory Method). Такой метод будет проверять корректность переданных аргументов до момента создания объекта, а лишь убедившись в их правильности, вызовет оператор mew и конструктор
student.hpp
#ifndef _STUDENT_HPP_
#define _STUDENT_HPP_
class Student
{ // ...
// Закрытый конструктор
Student ( const char * _lastName, int _mark );
public:
// Статический метод-фабрика
static Student* Make ( const char * _lastName, int _mark )
// ...
};
#endif // _STUDENT_HPP_
student.cpp
// ..
// Реализация конструктора
Student::Student ( const char * _lastName, int _mark )
: m_LastName( _lastName ), m_Mark( _mark )
{
}
// Реализация статического метода-фабрики
Student * Student::Make ( const char * _lastName, int _mark )
{
// Делаем проверку ограничения на оценки
if ( _mark >= 1 && _mark <= 100 )
// Все корректно, можно создавать объект
return new Student( _lastName, _mark );
else
{ // Сигнализируем об ошибке, не создаем объект
assert( !"Invalid student data" );
return nullptr;
}
}
// ...
test.cpp
#include “student.hpp”
int main ()
{
// Создаем студента с корректным баллом через метод-фабрику.
// Ожидаем успех
Student * pStudent = Student::Make( "Ivanov", 75 );
assert( pStudent );
delete pStudent;
// Создаем студента с превышающим допустимый баллом через метод-фабрику.
// Ожидаем неудачу
pStudent = Student::Make( “Petrov”, 101 );
assert( ! pStudent );
// Создаем студента со слишком маленьким баллом через метод-фабрику.
// Ожидаем неудачу
pStudent = Student::Make( “Sidorov”, 0 );
assert( ! pStudent );
}
18. Типовое решение Singleton на основе статических членов классов.
Может существовать необходимость в создании специального подвида классов, гарантирующих, что на их основе будет порожден единственный объект. Такая ситуация возникает как при моделировании понятий предметной области (может быть только один центральный банк, одна файловая система, один реестр Windows), так и создании управляющих классов (внутри программы может быть только один экземпляр класса-приложения, один объект доступа к периферийному устройству).
Такое ограничение можно выразить в явном виде при помощи статических членов и закрытого конструктора. Класс должен хранить адрес единственного создаваемого объекта, контролировать момент его создания в статическом методе доступа, полностью запрещать конструирование объектов напрямую, а также отвечать за его уничтожение.
Классы, обладающие такими свойствами, называют ОДИНОЧКАМИ (Singleton).
Ярким примером одиночки может быть класс, реализующий работу с принтером.
При этом нельзя допустить, чтобы в программе существовало более одного объекта, управляющего активным принтером. В приведенном ниже примере точкой доступа к принтеру является статический метод GetInstance, который при первом обращении создает объект и запоминает его адрес в статическом поле ms_Instance. Также происходит подписка на автоматическое удаление объекта-одиночки, используя механизм atexit.
printer.hpp
#ifndef _PRINTER_HPP_
#define _PRINTER_HPP_
class Printer
{ // Статическая переменная-чден, хранит указатель на созданный объект-одиночку
static Printer * ms_Instance;
// Закрытый конструктор по умолчанию
Printer ();
// Закрытый деструктор
~Printer ();
// Закрытый конструктор копий и оператор присвоения (реализации не будет)
Printer ( const Printer & );
Printer& operator = ( const Printer & );
// Статический метод уничтожения объекта-одиночки
static void DestroyInstance ();
public:
// Статический метод доступа к объекту-одиночке
static Printer * GetInstance ();
// Обычный полезный метод
void Print ( const char * _filePath );
};
#endif // _PRINTER_HPP_
printer.cpp
#include “printer.hpp”
// Определение статической переменной-члена, nullptr по умолчанию
Printer * Printer::ms_Instance;
// Реализация статического метода доступа
Printer * Printer::GetInstance ()
{ // Если это первый вызов, то объекта-одиночки еще не существует,
// и его нужно создать
if ( ! ms_Instance )
{ // Создаем объект-одиночку и запоминаем его адрес
// в статической переменной-члене
ms_Instance = new Printer();
// Подписываемся на вызов функции уничтожения после выхода из main
atexit( & Printer::DestroyInstance );
}
// В данной точке объект-одиночка гарантированно существует
return ms_Instance;
}
// Реализация статического метода уничтожения одиночки
void Printer::DestroyInstance ()
{ delete ms_Instance;
ms_Instance = nullptr;
}
// Реализация конструктора по умолчанию
Printer::Printer ()
{ // Выполняем захват необходимых ресурсов операционной системы
// ...
}
Printer::~Printer ()
{ // Выполняем освобождение захваченных ресурсов операционной системы
// ...
}
void Printer::Print ( const char * _filePath )
{ // Делаем низкоуровневые сложные действия, реализующие печать файла
// ...
}
test.cpp
#include “printer.hpp”
int main ()
{ // Получаем доступ к объекту-одиночке. Он будет автоматически создан
Printer* printer = Printer::GetInstance();
// Печатаем документ
printer->Print( "report.doc" );
}
// После завершения main будет вызвана функция Printer::DestroyInstance,
// которая уничтожит объект-одиночку
