Я хотел создать template <typename T> class InitConst
, некоторые элементы которого являются массивами T
. Я хотел заполнить эти массивы во время инициализации объектов класса, а затем быть уверенным, что эти массивы больше не изменятся, поэтому я определил член const T* const v
для каждого такого массива (я знаю, что первый const относится к элементам, а второй к указатели).
Поэкспериментировав с этим, я пришел к выводу, что определение "v" как "константный указатель на const" вынудило меня выделить и заполнить соответствующий массив, прежде чем окончательно инициализировать "v" адресом этого массива. Более того, поскольку я не могу инициализировать "v" внутри тела конструктора класса "InitConst", я пришел к выводу, что мне нужна вспомогательная функция, роль которой заключалась в том, чтобы (а) получить аргументы конструктора, (б) выделить и заполнить массивы и (c) использовать некоторые статические указатели для хранения адресов этих массивов. Эти статические указатели, наконец, будут использоваться конструктором для инициализации константных указателей, поэтому я получил такой код:
template <typename T>
class InitConst
{
public:
const T* const v1;
const T* const v2;
const T* const v3;
InitConst(T a1, T a2, T a3, int n)
: v1( init(a1,a2,a3,n) ), v2(init_p.temp_v2), v3(init_p.temp_v3)
{ }
private:
struct Tinit {
T* temp_v1;
T* temp_v2;
T* temp_v3;
};
static Tinit init_p;
T* init (T a1, T a2, T a3, int n)
{
init_p.temp_v1 = new T[n];
init_p.temp_v2 = new T[n];
init_p.temp_v3 = new T[n];
// populate "temp_v1", "temp_v2" and "temp_v3" using the method's arguments.
return init_p.temp_v1;
} // End method init.
}; // End class InitConst.
template <typename T>
typename InitConst<T>::Tinit InitConst<T>::init_p;
Этот код компилируется и работает, как и ожидалось, но я считаю этот дизайн искаженным: я привык к простому коду, где я сначала выделяю массив, затем вычисляю его элементы (оба действия обычно происходят в теле конструктора), а затем использую массив. Выше, напротив, конструктор почти ничего не делает сам по себе: его роль передается методу «init», который фактически создает массивы и использует некоторые вспомогательные указатели, чтобы передать их обратно конструктору.
Я тогда удивляюсь:
a) Необходим ли этот дизайн (или что-то подобное), если я решу объявить каждый указатель как «константный указатель на константу», или есть более чистый способ сделать это?
б) Объявление каждого указателя как «константный указатель на константу» было способом защиты от неправильного использования класса, но, возможно, мне это не нужно. Несколько менее строгий подход состоял бы в том, чтобы объявить "v1" и его братьев и сестер как частные члены, чтобы их нельзя было изменить вне класса. Однако это также предотвратит их чтение из-за пределов класса, и я не хочу иметь метод «read_vx» для каждого массива «vx». Что я тогда мог с этим поделать, то есть какой подход привел бы к более читаемому коду и при этом гарантировал бы как минимум невозможность изменения массивов извне класса?
Заранее спасибо, и извините за длинную прозу.
Редактировать: как я прокомментировал ниже, в моем реальном коде различные массивы, которые я хочу вычислить, гораздо эффективнее вычисляются вместе, и именно поэтому я использую одну функцию «инициализации». Аргументы для «init» («a1», «a2», «a3»), которые я предоставил в примере, на самом деле вводили в заблуждение.