В книге, которую я читаю, есть пример класса, который используется для объяснения понятий:
class Gameboard{
public:
Gameboard(int inWidth, int inHeight);
Gameboard(const Spreadsheet& src);
Gameboard& operator=(const Spreadsheet& rhs);
private:
GamePiece** mCells;
size_t width;
size_t height;
};
Затем они вводят шаблоны и представляют обновленный класс:
template<typename T>
class Grid{
public:
Grid<T>(int inWidth, int inHeight);
Grid<T>(const T& src);
Grid<T>& operator=(const T& rhs);
private:
T** mCells;
size_t width;
size_t height;
};
наконец, они вводят нестандартные параметры шаблона и говорят, что теперь вы можете сделать это:
template<typename T, size_t WIDTH, size_t HEIGHT>
class Grid{
public:
Grid<T>();
Grid<T>(const T& src);
Grid<T>& operator=(const T& rhs);
private:
T mCells[WIDTH][HEIGHT];
};
Из книги:
В классе шаблона Grid вы могли бы использовать нетипичные параметры шаблона, чтобы указать высоту и ширину сетки вместо того, чтобы указывать их в конструкторе. Принципиальное преимущество для указания не типовых параметров в списке шаблонов вместо конструктора состоит в том, что значения известны до компиляции кода. Напомним, что компилятор генерирует код для шаблонизированных методов, подставляя его в параметры шаблона перед компиляцией. Таким образом, вы можете использовать обычный двумерный массив в своей реализации вместо его динамического выделения..
Я не испытываю восторга от такого подхода к динамическому распределению памяти. Во-первых, означает ли это, что многомерный массив будет находиться в стеке (поскольку они, похоже, предполагают, что он не будет динамически размещаться)? Я не понимаю, почему вы не хотите динамически распределять память в куче?
Во-вторых, есть ли какое-то правило C ++ (которое я забыл), которое запрещает объявлять многомерный массив в стеке, отсюда и волнение при таком подходе?
Я пытаюсь понять, в чем преимущество использования не типовых параметров шаблона в их примере.
Во-первых, означает ли это, что многомерный массив будет находиться в стеке (поскольку они, похоже, предполагают, что он не будет динамически размещаться)?
Как вы можете видеть, ваш массив является непосредственным членом, он не косвенно указывает на указатель.
T mCells[WIDTH][HEIGHT];
Но было бы неправильно говорить, что он находится в стеке или в куче, так как на самом деле массив является частью вашего объекта, и где он находится, зависит от того, где и как расположен ваш объект. Если объект размещен в куче, его подобъект массива будет тоже, если весь объект находится в стеке, то и массив будет.
Я не понимаю, почему вы не хотите динамически распределять память в куче?
Вы могли бы. Но наличие массива new’d медленнее и более подвержено ошибкам (то есть его следует удалить и т. Д.).
Во-вторых, есть ли какое-то правило C ++ (которое я забыл), которое запрещает объявлять многомерный массив в стеке, отсюда и волнение при таком подходе?
Нет. Но размер любого массива должен быть константой времени компиляции. Нетипичные параметры шаблона — это как раз то, что — константы времени компиляции. Если бы они были просто параметрами функции типа int, компилятор не знал бы их значения, и было бы недопустимо создавать массив (многомерный или другой) в стеке.
«Во-первых, означает ли это, что многомерный массив будет в стеке»
Если Gameboard
находится в стеке (т.е. это локальная переменная), тогда да, массив также находится в стеке.
«Я не понимаю, почему вы не хотите динамически распределять память в куче?»
Для скорости. В этом случае, как Gameboard
вероятно останется долго, это не нужно. На самом деле, std::vector<std::vector<GamePiece>> mCells
было бы лучше, чем ручные массивы.
«Есть ли какое-то правило C ++ (которое я забыл), которое запрещает объявлять многомерный массив в стеке»
Нет, это допустимо Но, как и в случае с обычными массивами, размеры должны быть известны во время компиляции.
«Я пытаюсь понять, в чем заключается преимущество использования не типовых параметров шаблона в их примере».
Это надуманный пример.
Предположим, вы хотите создать целочисленный класс произвольной точности. Чем выше точность, тем больше места требуется. Этот класс может создаваться и уничтожаться часто, как обычная целочисленная переменная. Если данные распределяются динамически, это происходит медленнее. Задавая необходимую точность с помощью параметра шаблона, можно создать регулярный массив требуемого размера в качестве члена класса. Всякий раз, когда класс помещается в стек, массив также будет расти, и это будет быстрее.
Без ссылки на конкретные образцы и источники вы даете:
Я не испытываю восторга от такого подхода к динамическому распределению памяти
Потому что для этого вообще не требуется никакого динамического выделения, даже если вы создаете экземпляры в стеке.
Я не понимаю, почему вы не хотите динамически распределять память в куче?
Я часто работаю над небольшими встроенными системами, где у меня иногда даже нет возможности динамического управления памятью (или я не хочу нести накладные расходы). Для таких систем, и где вы очень хорошо знаете заранее, какие размеры вы можете / хотите на самом деле иметь, это довольно хорошая абстракция конфигурации, которую вы хотите использовать для реализаций конкретной целевой платформы.
Кроме вышеупомянутых причин, динамическое выделение приводит к снижению производительности во время выполнения, что нежелательно для приложений, критичных к производительности (например, движков рендеринга игровой среды).
В общем и целом:
Если у вас есть что-то, что может быть точно настроено во время компиляции, предпочтите это настройке во время выполнения.