Чтение онлайн

на главную - закладки

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

public:

SquareMatrix // передать базовому классу размерность

:SquareMatrixBase<t>(n, data) {} // матрицы и указатель на данные

...

private:

T data(n*n);

};

Объекты такого типа не нуждаются в динамическом выделении памяти, но зато могут быть очень большими. Вместо этого можно выделять память для данных матрицы из кучи:

template<typename T, size_t size>

class SquareMatrix: private SquareMatrixBase {

public:

SquareMatrix //
присвоить указателю на данные

:SquareMatrixBase<t>(n, 0), // в базовом классе значение null

pData(new T(n*n)) // выделить память для данных матрицы,

{this->setDataPtr(pData.get;} // сохранить указатель на нее и передать

... // его копию базовому классу

private:

boost::scoped_array<T> pData; // о классе boost::scoped_array

}; // см. правило 13

Независимо от того, где хранятся данные, с точки зрения «разбухания» кода важно лишь, что теперь многие (быть может, все) функции-члены SquareMatrix оказываются просто встроенными вызовами их версий из базового класса, которые теперь будут разделяются всеми матрицами, содержащими данные одного и того же типа, независимо от их размера. В то же время объекты SquareMatrix разных размеров относятся к разным типам. Поэтому, несмотря на то что классы SquareMatrix<double, 5> и SquareMatrix<double, 10> пользуются одними и теми же функциями, определенными в SquareMatrixBase<double>, не получится передать функции, ожидающей параметра типа SquareMatrix<double, 10>, объект типа SquareMatrix<double, 5>. Хорошо, не правда ли?

Да, хорошо, но не бесплатно. Для функции invert с жестко «зашитой» в исходный текст размерностью матрицы, скорее всего, был бы сгенерирован более эффективный код, чем разделяемой функции, которой размерность передается в качестве параметра либо хранится в самом объекте. Например, «зашитая» размерность может быть константой времени компиляции, так что к ней будут применимы различные виды оптимизации, в частности встраивание константы непосредственно в машинную команду в виде непосредственного операнда. Для функции, не зависящей от размерности, такой номер не пройдет.

С другой стороны, наличие только одной версии invert для разных размерностей уменьшает объем исполняемого кода, а это, в свою очередь, уменьшит размер рабочего множества программы и улучшит локальность ссылок в кэше команд. Это может ускорить исполнение программы настолько, что все потери эффективности по сравнению с зависящей от размерности версией будут с лихвой компенсированы. Какой эффект окажется доминирующим? Единственный способ получить ответ – попробовать оба варианта и исследовать поведение на вашей конкретной платформе с репрезентативными наборами данных.

Другой фактор, влияющий на эффективность, – это размеры объектов. Если вы не будете внимательны, то перенос независимых от размерности функций в базовый класс может привести к увеличению размера каждого объекта. Например, в только что приведенном коде для каждого объекта SquareMatrix имеется указатель на его данные в классе SquareMatrixBase, несмотря даже на то, что производный класс и так может получить эти данные. Это увеличивает размер каждого объекта SquareMatrix, по крайней мере, на размер указателя. Можно модифицировать класс так, чтобы необходимость в этих указателях отпала, но это компромисс. Например, если завести в базовом классе защищенный член для хранения указателя на данные матрицы, то мы пожертвуем инкапсуляцией (см. правило 22). Это также

может привести к усложнению алгоритмов управления ресурсами. Если в базовом классе хранится указатель на данные матрицы, то память для этих данных может быть либо выделена динамически, либо физически находиться внутри объекта производного класса (как мы видели). Так как же базовый класс определит, следует ли удалять указатель? Ответы на такие вопросы существуют, но чем изощреннее ваш дизайн, тем сложнее все получается. В некоторый момент умеренное дублирование кода может даже показаться спасением.

В этом правиле мы обсуждаем только разбухание кода из-за параметров шаблонов, не являющихся типами, но и параметры-типы могут привести к тому же. Например, на многих платформах int и long имеют одно и то же двоичное представление, поэтому функции-члены, скажем, для vector<int> и vector<long>, могут оказаться идентичными – разбухание в чистом виде. Некоторые компоновщики объединяют идентичные реализации функций, а некоторые – нет, и, значит, некоторые шаблоны, конкретизированные для int и для long, на одних платформах приводят к разбуханию, а на других – нет. Аналогично на большинстве платформ все типы указателей имеют одинаковое двоичное представление, поэтому шаблоны с параметрами указательных типов (например, list<int*>, list<const int*>, list<SquareMatrix<long,3>*> и т. п.) зачастую могли бы использовать общие реализации всех функций-членов. Как правило, это означает, что функции-члены, которые работают со строго типизованными указателями (например, T*) должны внутри себя вызывать функции, работающие с нетипизированными указателями (то есть void*). В некоторых реализациях стандартной библиотеки C++ такой подход применен к шаблонам vector, deque и list и им подобным. Если вас беспокоит опасность разбухания кода из-за использования шаблонов, возможно, стоит поступить аналогично.

Что следует помнить

• Шаблоны генерируют множество классов и функций, поэтому любой встречающийся в шаблоне код, который не зависит от параметров шаблона, приводит к разбуханию кода.

• Разбухания из-за параметров шаблонов, не являющихся типами, часто можно избежать, заменив параметры шаблонов параметрами функций или данными-членами класса.

• Разбухание из-за параметров-типов можно ограничить, обеспечив общие реализации для случаев, когда шаблон конкретизируется типами с одинаковым двоичным представлением.

Правило 45: Разрабатывайте шаблоны функций-членов так, чтобы они принимали «все совместимые типы»

Интеллектуальные указатели – это объекты, которые ведут себя во многом подобно обычным указателям, но добавляют функциональность, которую последние не предоставляют. Например, в правиле 13 объясняется, как можно использовать стандартные классы auto_ptr и tr1::shared_ptr для автоматического удаления динамически выделенных ресурсов в нужное время. Итераторы STL-контейнеров почти всегда являются интеллектуальными указателями. Понятно, что от обычного указателя нельзя ожидать, что он будет сдвигаться на следующий узел связанного списка в результате выполнения операции «++», но итератор списка list::iterator работает именно так.

Для чего обычные указатели хороши – так это для поддержки неявных преобразований типов. Указатели на объекты производных классов неявно преобразуются в указатели на объекты базовых классов, указатели на неконстантные объекты – в указатели на константные и т. п. Например, рассмотрим некоторые преобразования, которые могут происходить в трехуровневой иерархии:

class Top {...};

class Middle: public Top {...};

class Bottom: public Middle {...};

Поделиться:
Популярные книги

Воевода

Ланцов Михаил Алексеевич
5. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Воевода

Девятый

Каменистый Артем
1. Девятый
Фантастика:
боевая фантастика
попаданцы
9.15
рейтинг книги
Девятый

Совершенный: пробуждение

Vector
1. Совершенный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Совершенный: пробуждение

Кодекс Крови. Книга Х

Борзых М.
10. РОС: Кодекс Крови
Фантастика:
фэнтези
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга Х

Дайте поспать! Том IV

Матисов Павел
4. Вечный Сон
Фантастика:
городское фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Дайте поспать! Том IV

Ротмистр Гордеев

Дашко Дмитрий Николаевич
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Ротмистр Гордеев

Как я строил магическую империю 2

Зубов Константин
2. Как я строил магическую империю
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Как я строил магическую империю 2

Тройняшки не по плану. Идеальный генофонд

Лесневская Вероника
Роковые подмены
Любовные романы:
современные любовные романы
6.80
рейтинг книги
Тройняшки не по плану. Идеальный генофонд

Специалист

Кораблев Родион
17. Другая сторона
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Специалист

Не грози Дубровскому! Том IX

Панарин Антон
9. РОС: Не грози Дубровскому!
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Не грози Дубровскому! Том IX

Неудержимый. Книга III

Боярский Андрей
3. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга III

Изгой. Пенталогия

Михайлов Дем Алексеевич
Изгой
Фантастика:
фэнтези
9.01
рейтинг книги
Изгой. Пенталогия

Жена по ошибке

Ардова Алиса
Любовные романы:
любовно-фантастические романы
7.71
рейтинг книги
Жена по ошибке

Пистоль и шпага

Дроздов Анатолий Федорович
2. Штуцер и тесак
Фантастика:
альтернативная история
8.28
рейтинг книги
Пистоль и шпага