- •Ооп: Лекция 10. Более сложные формы композиции.
- •Типичные ошибки при реализации родительских классов
- •Рекомендованный стиль интерфейса родительских классов
- •Классы-родители с применением итераторов контейнеров
- •Умные указатели std::unique_ptr
- •Использование std::unique_ptr для композиции объектов
- •Итерирование дочерних элементов с учетом умных указателей
- •Использование средства std::function
- •Использование отображений при реализации композиции
- •Использование множеств
- •Композиция с кратностью многие-ко-многим
- •Полные примеры из лекции
Использование отображений при реализации композиции
Когда родительский объект начинает содержать большое количество дочерних объектов, начинает хромать производительность алгоритмов поиска. Пусть имеется название главы, требуется найти ее в составе книги. Использование простого контейнера, такого как std::vector, для хранения глав означает, что реализация поиска будет основана на линейном переборе элементов. Как только число сканируемых элементов превысит 10, недостатки этого подхода могут стать заметными.
Задачу поиска главы по известному названию можно решить с применением дополнительного контейнера-отображения, не отменяющего хранение глав в векторе. Ключом в таком отображении будет строка, а значением - указатель на главу. Поскольку итерировать главы в алфавитном порядке не требуется, логично выбрать контейнер std::unordered_map. Внесение таких изменений также позволяет добавить полезную проверку - в книге не должно быть двух глав с одинаковым названием. Ниже представлен набросок кода предлагаемых изменений:
book.hpp
#include <unordered_map>
class Book
{
public:
// Метод для поиска главы по названию
Chapter const * findChapterByTitle ( std::string const & _title ) const;
// ...
private:
// ...
// Отображение для ускорение поиска: ключ - название главы, значение - глава
std::unordered_map< std::string, Chapter const * > m_chaptersByTitle;
};
book.cpp
/*****************************************************************************/
// Реализация метода для поиска главы по названию
Chapter const *
Book::findChapterByTitle ( std::string const & _title ) const
{
// Ищем главу во вспомогательном отображении
auto it = m_chaptersByTitle.find( _title );
return ( it != m_chaptersByTitle.end() ) ? it->second : nullptr;
}
/*****************************************************************************/
// Реализация метода добавления главы
void Book::addChapter ( std::unique_ptr< Chapter > _chapter )
{
// Проверка на уникальность названия главы
if ( findChapterByTitle( _chapter->getTitle() ) )
throw std::logic_error( "Chapter with duplicate title" );
// Регистрация главы во вспомогательном отображении
m_chaptersByTitle[ _chapter->getTitle() ] = _chapter.get();
// Регистрация главы в основном контейнере
m_chapters.push_back( std::move( _chapter ) );
}
/*****************************************************************************/
// Реализация метода удаления главы
void Book::removeChapter ( Chapter const & _chapter )
{
// Проверять наличие главы в книге теперь проще по названию
auto itByTitle = m_chaptersByTitle.find( _chapter.getTitle() );
if ( itByTitle == m_chaptersByTitle.end() )
// Ошибка: глава не найдена в книге
throw std::logic_error( "Chapter does not exists in book" );
// Удаляем главу из вспомогательного отображения
m_chaptersByTitle.erase( itByTitle );
// Удаляем главу из основного контейнера
int nChapters = getChaptersCount();
for ( int i = 0; i < nChapters; i++ )
if ( m_chapters[ i ].get() == &_chapter )
{
m_chaptersByTitle.erase( _chapter.getTitle() ); // удалит и уничтожит главу
return;
}
}
/*****************************************************************************/
// Реализация метода очистки книги от глав
void Book::clearChapters ()
{
// Сбрасываем оба контейнера
m_chaptersByTitle.clear();
m_chapters.clear();
}
/*****************************************************************************/
