
- •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. Необычный рекуррентный шаблон. Структура, варианты применения.
16. Статические переменные-члены. Цель применения. Синтаксис. Особенности компоновки.
Обычные переменные-члены размещаются в памяти объектов класса, и каждый созданный объект содержит собственный набор переменных-членов, объявленных в теле класса. Область видимости таких переменных-членов ограничивается телом класса, включая выносные реализации функций-членов. Время жизни обычной переменной-члена определяется временем жизни содержащего их объекта.
На практике встречается необходимость в переменных-членах иного рода — относящихся к классу в целом, а не к конкретному объекту. Такие переменные-члены называются СТАТИЧЕСКИМИ. Соответственно, обычные переменные-члены, относящиеся к объектам, иногда называют НЕСТАТИЧЕСКИМИ. Статические и нестатические переменные-члены объединяет общая область видимости, одинаковое влияние спецификаторов доступа (public, private,..).
Однако время жизни статических переменных-членов принципиально отличается от нестатических. Местом хранения статических переменных-членов в памяти является сегмент данных, что соответствует глобальным переменным. Отсюда вытекает идентичное глобальным переменным время жизни — они создаются до начала выполнения функции main() и уничтожаются после ее завершения.
Сколько бы ни было создано объектов класса, статическая переменная-член существует в памяти строго в единственном экземпляре. Этот экземпляр существует даже если не было фактически создано ни одного объекта класса. Очевидно, наличие статических переменных-членов в классе ни каким образом не увеличивает размеры объектов.
Единственным синтаксическим различием объявления статических и нестатических переменных-членов класса является наличие ключевого слова static. В приведенном ниже простейшем примере, статическая переменная-член ms_ObjCounter используется для подсчета количества созданных за все время выполнения программы объектов некоторого класса. В свою очередь, обычная нестатическая переменная-членm_Data содержит некоторое полезное данное, относящееся к каждому из объектов:
class Test
{
// Статическая переменная-член (храним независимо от объектов в сегменте данных)
static int ms_ObjCounter;
// Обычная (нестатическая) переменная-член (храним в объекте)
int m_Data;
public:
// Конструктор
Test ( int _data )
: m_Data( _data )
{
// Увеличиваем счетчик, хранящийся в статической переменной-члене
ms_ObjCounter++;
}
// Конструктор копий
Test ( const Test & _test )
: m_Data( _test.m_Data )
{
// Также увеличиваем счетчик
ms_ObjCounter++;
}
};
Из внутренних функций-членов класса обращение к статическим переменным-членам непосредственно по имени ничем не отличается от обращения к обычным переменным-членам.
При условии, что статическая переменная-член была объявлена в зоне спецификатора доступа public, возможно обращение из функций, определенных за пределами рассматриваемого класса. Обращение возможно записать двумя способами:
подобно нестатическим членам, через любой из объектов класса + точка, либо через указатель на объект класса + стрелка:
Test t;
std::cout << t.ms_ObjCounter;
Test * pTest = & t;
std::cout << pTest->ms_objCounter;
через имя класса (только для статических членов!):
std::cout << Test::ms_objCounter;
Второй способ является предпочтительным, поскольку подчеркивает отношение статической переменной-члена к классу, а не к объекту, к тому же не требует наличия готового объекта или создания временного лишь для обращения к статическому члену. Первый способ с технической точки зрения допустим, однако сбивает с толку при чтении кода, поскольку визуально неразличим от обращения к нестатическим членам.
Объявление статической переменной-члена в классе не является определением, соответственно где-либо в программе обязательно должно существовать ее определение. Из хранения статических переменных-членов в сегменте данных вытекает обязательное требование дополнительного определения таких переменных в глобальной области в одной из единиц трансляции. В любом компилируемом С++ файле следует разместить следующее определение:
int Test::ms_ObjCounter;
Ключевое слово static в определении повторно использовать не нужно.
Как и для глобальных переменных, статическим членам присваивается нулевое значение по умолчанию. Если тип статического члена сам является структурой или классом, вызывается конструктор по умолчанию. Переменные встроенных типов автоматически получат нулевое значение. При необходимости, определение может определять собственное значение, если оно должно отличаться от автоматически присваиваемого:
int Test::ms_ObjCounter = 5;
Если в программе нигде не будет размещено подобное определение для статической переменной-члена, компиляция по-прежнему пройдет успешно, однако возникнет ошибка на этапе компоновки:
error LNK2001: unresolved external symbol "private: static int Test::ms_ObjCounter" (?ms_ObjCounter@Test@@0HA)
Для компилятора не имеет никакого значения в какой единице трансляции будет размещено определение статической переменной-члена. Типичная конвенция разбиения определений классов на заголовочный файл и файл реализации предполагает размещение определения статических членов в файле реализации того же класса. Однако, конвенции, в отличие от правил, соблюдаются программистами добровольно, по сути, являются общепринятой условностью. Соответственно, разбиение программы на файлы объявления, реализации классов и другие файлы в принципе не имеет для компилятора никакого значения. Отсюда следует, что решение в каком именно файле разместить определение статического члена определяется исключительно программистом. И разместить его в файле реализации того же класса является ожидаемым и логичным.
Ни в коем случае не следует размещать определение статических членов в заголовочных файлах. Такие файлы могут включаться сразу в несколько единиц трансляции при помощи директив #include, и размещение в них определений статических членов приведет к другой ошибке этапа компоновки — к множественному определению одной и той же переменной в нескольких единицах трансляции.
Особый случай статических переменных-членов представляют собой константы целочисленных типов (char, short, int, long, а также перечисления). Подобно глобальным константам, такие статические константные члены подпадают под правила внутренней компоновки (internal linkage). Соответственно, их можно инициализировать сразу в заголовочной части класса, поскольку при таком типе компоновки допускается наличие нескольких копий одного и того же определения в различных единицах трансляции. При помещении в заголовочную часть класса, предполагается, что инициализирующее выражение будет возможно вычислить во время компиляции. В приведенном ниже примере константа целочисленного типа получает значение сразу при объявлении, и используется для задания размера массива в объектах:
class Queue
{
static const int QUEUE_LENGTH = 100;
int m_QueueData[ QUEUE_LENGTH ];
};
Стандарты языка С++, предшествовавшие недавно принятому стандарту С++'11, в явном виде запрещали инициализацию таким образом для констант неинтегральных типов - действительных чисел, константных массивов и константных объектов классов. Новая версия стандарта снимает это ограничение, при условии, что все инициализирующие выражения представляется возможным вычислить во время компиляции (к сожалению, это поддерживается пока не всеми компиляторами).