Question

I wanted to create a template <typename T> class InitConst some of whose members are arrays of T. I wanted to populate these arrays during the class' objects initialization and then be sure that these arrays would not change anymore, so I defined a member const T* const v for each such array (I know that the first const refers to the elements and the second to the pointers).

Experimenting with this made me conclude that defining "v" to be "const pointer to const" obliged me to allocate and populate the corresponding array before finally initializing "v" with the address of this array. Moreover, since I cannot initialize "v" inside the body of the constructor of class "InitConst", I concluded that I needed an auxiliary function, whose role was to (a) get the constructors arguments, (b) allocate and populate the arrays, and (c) use some static pointers to hold the addresses of these arrays. These static pointers would finally be used by the constructor to initialize the const pointers, so I got a code like this:

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;

This code compiles and works as expected, but I consider this design contorted: I am used to simple code, where I first allocate an array, then compute its elements (both things usually happen in the constructor's body) and then use the array. Above, in contrast, the constructor does almost nothing by itself: its role is transferred to method "init", which actually creates the arrays and uses some auxiliary pointers to communicate them back to the constructor.

I then wonder:

a) Is this design (or something similar) necessary if I decide do declare each pointer as "const pointer to const", or there is a cleaner way to do it?

b) Declaring each pointer as "const pointer to const" was a way to protect against misuses of the class, but perhaps I do not need that much. A somewhat less strict approach would have been to declare "v1" and its siblings as private members, so that they could not be changed from outside of the class. However, doing so would also prevent them from being read from outside of the class, and I do not want to have a "read_vx" method for each array "vx". What could I then do about it, that is, what approach would lead to a more readable code and still guarantee at least that the arrays cannot be changed from outside of the class?

Thanks in advance, and sorry for the long prose.

Edit: as I commented below, in my real code, the various arrays that I want to compute are much more efficiently computed all together, and that is why I use a single "init" function. The arguments to "init" ("a1", "a2", "a3") that I supplied in the example were actually misleading.

Was it helpful?

Solution

Based on the update to your question you can still handle the initialization cleanly and reduce the code further than what I previously proposed. Since you know the data is writable you can use const_cast and do the initialization in the constructor.

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
    }
};



[NOTE: The following solution is not usable based on comments from the OP]

It would be better to have init assist in the initialization of a single member variable at a time rather than all three. This would reduce the code significantly and eliminate the need for the static variable which will need to be defined for each type of T used with InitConst. You could also change init to be static to prevent the accidental use of member variables that have not been initialized.

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
    }
};

OTHER TIPS

There is no need for 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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top