Инициализация const массива const в шаблонном классе C ++

Я хотел создать template <typename T> class InitConst некоторые из членов которых являются массивами T, Я хотел заполнить эти массивы во время инициализации объектов класса, а затем быть уверенным, что эти массивы больше не изменятся, поэтому я определил член const T* const v для каждого такого массива (я знаю, что первый const относится к элементам, а второй к указателям).

Экспериментируя с этим, я пришел к выводу, что определение «v» как «const указатель на 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», который фактически создает массивы и использует некоторые вспомогательные указатели для передачи их обратно конструктору.

Я тогда удивляюсь:

а) Нужен ли этот дизайн (или что-то подобное), если я решу объявить каждый указатель как «указатель const на const», или есть более чистый способ сделать это?

б) Объявление каждого указателя как «const указатель на const» было способом защиты от неправильного использования класса, но, возможно, мне не нужно так много. Несколько менее строгим подходом было бы объявить «v1» и его братьев и сестер частными членами, чтобы их нельзя было изменить извне класса. Тем не менее, это также предотвратит их чтение из-за пределов класса, и я не хочу иметь метод «read_vx» для каждого массива «vx». Что я мог тогда сделать с этим, то есть, какой подход привел бы к более читаемому коду и все же гарантировал бы, по крайней мере, что массивы не могут быть изменены извне класса?

Заранее спасибо и простите за длинную прозу.

редактировать: как я прокомментировал ниже, в моем реальном коде различные массивы, которые я хочу вычислить, гораздо более эффективно вычисляются вместе, и поэтому я использую одну функцию «init». Аргументы «init» («a1», «a2», «a3»), которые я приводил в примере, на самом деле вводили в заблуждение.

1

Решение

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

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( new T[n] ), v2( new T[n] ), v3( new T[n] )
{
T* t1 = const_cast<T*>(v1);
T* t2 = const_cast<T*>(v2);
T* t3 = const_cast<T*>(v3);

//  do intialization here
}
};

[ПРИМЕЧАНИЕ: следующее решение не может быть использовано на основе комментариев от OP]

Было бы лучше иметь init помочь в инициализации не замужем переменная-член за раз, а не все три. Это значительно сократило бы код и избавило бы от необходимости static переменная, которая должна быть определена для каждый тип T используется с InitConst, Вы также можете изменить init быть статический для предотвращения случайного использования переменных-членов, которые не были инициализированы.

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,n) ), v2( init(a2,n)), v3(init(a3,n))
{ }

private:
static const T* init (T a, int n)
{
T* v = new T[n];
// populate "v" from "a"return v
}
};
2

Другие решения

Там нет необходимости struct Tinit:

tempalate<typename T>
InitConst::InitConst(T a1, T a2, T a3, int n)
: v1(init(a1,n)), v2(init(a2,n)), v3(init(a3,n))
{}

tempalate<typename T>
T* InitConst::init (T a, int n)
{
T* result = new T[n];
// TODO: populate result using a
return result
};

// TODO implement destructor, copy constuctor and
// copy assignement operator
0

По вопросам рекламы [email protected]