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

SquareMatrixBase(std::size_t n, T pMem) // сохраняет размерность

:size(n), pData(pMem){} // и указатель на данные матрицы

void setData(T *ptr) { pData = ptr;} // присвоить значение pData

...

private:

std::size_t size; // размерность матрицы

T *pData; // указатель на данные матрицы

};


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


templatetypename T, size_t size

class SquareMatrix: private SquareMatrixBase {

public:

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

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

...

private:

T data(n*n);

};


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


templatetypename T, size_t size

class SquareMatrix: private SquareMatrixBase {

public:

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

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

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

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

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

private:

boost::scoped_arrayT pData; // о классе boost::scoped_array

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


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

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

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

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже