- •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. Необычный рекуррентный шаблон. Структура, варианты применения.
34. Понятие интерфейса. Применение интерфейсов.
интерфейсы- разновидность абстрактных классов, состоящих только из чисто виртуальных функций. Допускается также определение статических констант и вложенных типов. Интерфейс не может содержать полей, конструкторов, невиртуальных методов либо виртуальных методов с телом. Интерфейс - это чистая абстракция без каких-либо элементов реализации.
В С++ же интерфейс - это скорее особый термин при рассуждении, типовое решение, а не явная языковая конструкция.
Преимущество использования интерфейсов состоит в том, что абстракция, инициирующая взаимодействия может не знать ничего о природе обеспечивающей его реализации. Это позволяет невероятным образом расщеплять зависимости между классами и подменять объекты в любой момент, пока они реализуют требуемый интерфейс.
Пример:
Следующее объявление вводит интерфейс слушателя изменений котировок биржевых акций (Stock):
stock_change_listener.hpp
#ifndef _STOCK_CHANGE_LISTENER_HPP_
#define _STOCK_CHANGE_LISTENER_HPP_
class Stock;
class StockChangeListener
{
public:
// Сообщение об изменении цены котировки указанных акций
virtual void onPriceChanged ( Stock & _s, double _oldPrice ) = 0;
};
#endif // _STOCK_CHANGE_LISTENER_HPP_
Любой класс, который унаследует данный интерфейс, может выступать в качестве получателя сообщений об изменениях цены на акции. Достаточно создать объект, класс которого реализует указанный интерфейс, и зарегистрировать его в классе, представляющем акции:
stock.hpp
#ifndef _STOCK_HPP_
#define _STOCK_HPP_
#include <vector>
class StockChangeListener;
// Класс, представляющий биржевую котировку акций
class Stock
{
private:
// Набор слушателей изменений цены акций
std::vector< StockChangeListener * > m_listeners;
// Название котировки ценных бумаг
const char * m_title;
// Текущая цена
double m_currentPrice;
private:
// Запрещенные конструктор копий и оператор присвоения
Stock ( const Stock & _s );
Stock & operator = ( const Stock & _s );
public:
// Конструктор
Stock ( const char * _title, double _initialPrice );
// Метод доступа к названию котировки
const char * getTitle () const;
// Метод доступа к текущей цене
double getCurrentPrice () const;
// Метод регистрации слушателея изменения цены
void addChangeListener ( StockChangeListener & _listener );
// Метод обновления цены
void updatePrice ( double _newPrice );
};
// Реализация метода доступа к названию котировки
inline const char *
Stock::getTitle () const
{
return m_title;
}
// Реализация метода доступа к текущей цене
inline double
Stock::getCurrentPrice () const
{
return m_currentPrice;
}
#endif // _STOCK_HPP_
35. Множественное наследование конкретных классов. Синтаксис, структура в памяти, особенности применения и реализации.
С++, в отличие от многих других объектно-ориентированных языков, допускает множественное наследование от конкретных классов. Базовые классы следует перечислять через запятую. Класс, производный от двух и более конкретных классов, объединяет свойства всех базовых. Такие классы иногда называют “примесями” (mixin).
imagebutton.hpp
#ifndef _IMAGEBUTTON_HPP_
#define _IMAGEBUTTON_HPP_
//************************************************************************
#include "button.hpp"
#include "imagecontrol.hpp"
//************************************************************************
// Класс, представляющий кнопку с изображением
class ImageButton
: public Button, public ImageControl
{
//------------------------------------------------------------------------
public:
// Конструктор - передаются аргументы для двух базовых классов
ImageButton ( const std::string & _text, const std::string & _imageFilename );
// Переопределение метода отрисовки из базового класса Button
void draw ( int _x, int _y ) override;
//------------------------------------------------------------------------
};
//************************************************************************
#endif // _IMAGEBUTTON_HPP_
imagebutton.cpp
//************************************************************************
#include "imagebutton.hpp"
//************************************************************************
ImageButton::ImageButton ( const std::string & _text,
const std::string & _imageFilename )
// Вызов конструкторов двух базовых классов
: Button( _text )
, ImageControl( _imageFilename )
{
}
//************************************************************************
// Реализация переопределенного метода отрисовки кнопки
void ImageButton::draw ( int _x, int _y )
{
// Вычисляем размеры текста
int width, height;
calculateTextSize( width, height );
// Изображение выводится слева от текста, понадобится 3 отступа
width += getImageWidth() + 3 * PADDING;
// Высота кнопки - максимум из высоты изображения и текста + 2 отступа
if ( getImageHeight() > height )
height = getImageHeight();
height += 2 * PADDING;
// Отрисовка фонового прямоугольника (вызов Button::drawRectangle)
drawRectangle( _x, _y, width, height );
// Отрисовка изображения (вызов ImageConrol::drawImage)
drawImage( _x + PADDING,_y + PADDING );
// Отрисовка текста на кнопке правее изображения (вызов Button::drawText)
drawText( _x + getImageWidth() + 2 * PADDING, _y + PADDING );
}
Структура размещения данных класса-примеси ImageButton в памяти предполагает, что в начале будут размещаться данные первого базового класса Button, затем второго ImageControl. Если бы ImageButton содержал собственные дополнительные поля, они бы размещались в объекте после полей обоих базовых классов:
Указатель или ссылка на объект класса ImageButton может быть преобразован к указателю/ссылке на объект любого из двух базовых классов:
ImageButton ib( “OK”, “ok.png” );
Button * pButton = & ib;
ImageControl * pControl = & ib;
Интересно, что, несмотря на манипулирование одним и тем же объектом ImageButton, абсолютные значения адресов в преобразованных указателях pButton и pControl совпадать не будут. Это вытекает из расположения полей базовых классов в памяти объекта. Поля первого базового класса находятся в начале объекта, и адрес pButton будет совпадать с адресом начала объекта. Но поля второго базового класса смещены от начала объекта на размер первого базового класса. Соответственно, этот адрес не совпадает с адресом начала объекта. Учитывая факт возможного смещения адресов в иерархии множественного наследования, следует всячески избегать попыток преобразования вниз по такой иерархии.