Встроенные вызовы не встроенных функций базового класса (что это значит точно)? - PullRequest
5 голосов
/ 12 октября 2011

Я читаю книгу по C ++ и нахожусь в разделе об уменьшении объектного кода, сгенерированного шаблонами ( Effective C ++ III, Скотт Мейерс ).Один из приведенных примеров:

template <typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T> {
    public:
        SquareMatrix()
            : SquareMatrixBase<T>(n, 0),
              pData(new T[n*n])
        { this->setDataPtr(pData.get()); }

        ... functions ...

    private:
        boost::scoped_array<T> pData;
};

, где базовый класс SquareMatrixBase имеет функцию с именем:

void invert(std::size_t matrixSize);

«Книга переходит к« Независимо отгде данные хранятся, ключевой результат с точки зрения раздувания состоит в том, что теперь многие - возможно, все - функции-члены SquareMatrix могут быть простыми встроенными вызовами не встроенных версий базового класса, которые совместно используются всеми другими матрицами, содержащимиодин и тот же тип данных, независимо от их размера. "

Что означает" встроенные вызовы не встроенных версий базового класса ... "? Если бы это был встроенный вызов, я бы подумалпоместил бы версию базового класса любой функции в место, где использовался inline, но это привело бы к тому же самому взлому кода, как я думал. Это говорит о том, что это преимущество против взлома кода.

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

|| РЕДАКТИРОВАТЬ - Дополнительная информация ||

Целью создания квадратной матрицы и квадратной матрицы в этом отрывке было:

SquareMatrix изначально был автономным шаблоном (не производным).Он содержал ряд функций, которые выполняли операции на основе значения параметра шаблона n.Таким образом, по существу была копия каждой функции для каждого используемого значения n (или для каждой пары n, T), поскольку для каждой пары параметров был создан новый шаблон с этими функциями.SquareMatrixBase был создан для перемещения функций, зависящих от параметра размера, в базовый класс.Поскольку базовый класс создается только с параметром типа (а не с размером), функции в базовом классе можно вызвать, передав значение размера, которое производный класс передает в конструктор базового класса.Это означает, что существует только одна версия функций для каждого typename T, переданного в шаблон SquareMatrix, независимо от переданного std :: size_t n (в отличие от одной версии каждой функции для каждой комбинации {T, n}.

Ответы [ 4 ]

6 голосов
/ 12 октября 2011

Дело в том, что SquareMatrix::invert() встроено, поэтому функция даже не появляется в результирующем коде, и вместо этого защищенная базовая функция SquareMatrixBase::invert(n) вызывается напрямую.

Теперь, так как эта функция не встроенный, есть только один экземпляр этой функции (для каждого типа T), а не одна копия для каждого размера n.Это резко контрастирует с дизайном для одного класса, где одна функция invert() будет создаваться для каждого значения n.

3 голосов
/ 12 октября 2011

Это означает, что (многие из) функций в классе SquareMatrix будут простыми встроенными функциями; и эти встроенные функции будут состоять из не встроенного вызова функции базового класса, которая реализует эту функциональность. Например, SquareMatrix может иметь член invert(), встроенный как:

void invert() {SquareMatrixBase<T>::invert(n);}

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

2 голосов
/ 12 октября 2011

Это означает, что производные классы будут содержать встроенные функции, которые будут вызывать (тяжелые) не встроенные функции в базовом классе, выполняющие реальную работу. Два сравниваемых варианта:

template <typename T, std::size_t n>
class SquareMatrix {
...
  void invert();
...
};

template <typename T, std::size_t n>
void SquareMatrix::invert() {
   ... heavy code ...
}

и

template <typename T, std::size_t n>
class SquareMatrix : SquareMatrixBase<T> {
...
  void invert() { SquareMatrixBase<T>::invert(pData.get(), n); }
...
};

template <typename T>
void SquareMatrixBase::invert(T* data, int n){
   ... heavy code ...
}

Теперь второй генерирует тяжелый код один раз для T, тогда как первый генерирует тяжелый код один раз для T и n. Это означает, что во втором случае больше (почти идентичного) кода.

Обратите внимание, что использование наследования для этого трюка не является обязательным, и я бы лично использовал вместо этого только бесплатные функции (возможно, в закрытом пространстве имен).

Редактировать: встраивание - это, в основном, замена вызова функции на тело функции (бета-сокращение, ха!). От

my_matrix.invert()

вы получите, например.

SquareMatrixBase<float>::invert(my_matrix.pData.get(), 3); <-- pseudocode

в коде вызова.

0 голосов
/ 12 октября 2011

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

Например, вы упомянули, что SquareMatrixBase имеет функцию с именем:

void SquareMatrixBase<T>::invert(std::size_t matrixSize);

В SquareMatrix вы можете просто вызвать эту функцию как:

template<typename T, std::size_t n>
inline void SquareMatrix<T, std::size_t n>::invert() { SquareMatrixBase<T>::invert(n); }

Встроенная версия кода, даже если она может быть реализована многими различными типами и размеров, будет компилироваться в один вызов функции ... «тяжелая» функция базового класса должна быть только может быть создан для каждого отдельного типа , а не для каждой перестановки каждого различного размера матрицы этого типа, может потребоваться создание экземпляра с ... так, что это определенно сократит количество кода. Так что SquareMatrixBase<T> нужно будет создавать только для таких типов, как int, double и т. Д. Он содержит все "основные" функции, которые используют много кода. SquareMatrix<T, std::size_t n> в другом классе можно создать не только для int, но и для int и размером {1, 2, 3, 4, ...}. Каждый отдельный аргумент шаблона для размера матрицы будет создавать новую версию SquareMatrix<T, std::size_t n>, но будет использовать только один экземпляр для int версии SquareMatrixBase<T>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...