Home > Software design >  How to initialize a pointer to an array in a class constructor?
How to initialize a pointer to an array in a class constructor?

Time:04-20

I have this code

template <class ItemType>
class ArrTest {
public:
    ArrTest();
private:
    ItemType* info;
};

template <class ItemType>
ArrTest<ItemType>::ArrTest()
{
    info = ItemType[50];
}

int main() {
    ArrTest<int> ar();
    return 0;
}

Right now when I try and build this I get

../src/test1.cpp:23:9: error: 'ItemType' does not refer to a value
        info = ItemType[50];

I don't understand how to initialize this into pointer. I believe its a pointer to the first item in the array. but then I should also be able to do info[3] for the 4th member of the array for instance.

CodePudding user response:

You need to use the new[] operator to allocate the array dynamically:

template <class ItemType>
ArrTest<ItemType>::ArrTest()
    : info(new ItemType[50])
{
}

And then, be sure to add a destructor to delete[]'s the array, and also add copy/move constructors and copy/move assignment operators, per the Rule of 3/5/0:

template <class ItemType>
class ArrTest {
public:
    ArrTest();
    ArrTest(const ArrTest &src);
    ArrTest(ArrTest &&src);
    ~ArrTest();
    ArrTest& operator=(ArrTest src);
private:
    ItemType* info;
};

template <class ItemType>
ArrTest<ItemType>::ArrTest()
    : info(new ItemType[50])
{
}

template <class ItemType>
ArrTest<ItemType>::ArrTest(const ArrTest &src)
    : info(new ItemType[50])
{
    std::copy(src.info, src.info   50, info);
}

template <class ItemType>
ArrTest<ItemType>::ArrTest(ArrTest &&src)
    : info(nullptr)
{
    std::swap(info, src.info);
}

template <class ItemType>
ArrTest<ItemType>::~ArrTest()
{
    delete[] info;
}

template <class ItemType>
ArrTest<ItemType>& ArrTest<ItemType>::operator=(ArrTest<ItemType> src)
{
    std::swap(info, src.info);
    return *this;
}

A better solution would be to use std::vector instead, and let it handle all of these details for you, eg:

#include <vector>

template <class ItemType>
class ArrTest {
public:
    ArrTest();
private:
    std::vector<ItemType> info;
};

template <class ItemType>
ArrTest<ItemType>::ArrTest()
    : info(50)
{
}

Either way, ArrTest<int> ar(); does not declare a default-constructed variable named ar of type ArrTest<int>, like you are expecting. It declares a function named ar that takes no parameters and returns an ArrTest<int>, which is not what you want.

To avoid that, you need to either:

  • drop the parenthesis:

    ArrTest<int> ar;

  • replace the parenthesis with curly braces:

    ArrTest<int> ar{};

CodePudding user response:

This construction

info = ItemType[50];

is syntactically invalid. ItemType is a type as for example int because it is a type template parameter. So you have similarly to

info = int[50];

but the right side expression is not an object. It is a type specifier. So the compiler issues a compilation error.

Pay attention to that if you will write something like the following

template <class ItemType>
ArrTest<ItemType>::ArrTest()
{
    ItemType a[50];
    info = a;
}

then the pointer info will have an invalid value because the local array a defined in the constructor will not be alive after exiting the constructor.

CodePudding user response:

You can use constructor initializer list and new, as shown below:

template <class ItemType>
//----------------------------vvvvvvvvvvvvvvvvvvvvvv-->added this 
ArrTest<ItemType>::ArrTest(): info(new ItemType[50])
{
  
}

Also note that ArrTest<int> ar(); declares a function named ar with the return type of ArrTest<int> and that takes no parameters. If your intention was to create an object of type ArrTest<int> then, you can replace it with:

ArrTest<int> ar{};

Also, don't forget to have a corresponding delete[] to free the allocated memory when no longer needed or use a std::vector so that you don't have to do manual memory management.

CodePudding user response:

Some answer suggested the use of new or, even better, std::vector. If the number element is fixed and known at compile-time (e.g. 50), you may consider a std::array:

#include <array>

template <class ItemType>
class ArrTest {
   public:
   private:
    std::array<ItemType, 50> info;
};

or a C-style array:

template <class ItemType>
class ArrTest {
   public:
   private:
    ItemType info[50];
};

With these solutions, you don't need to implement destructor, copy/move constructors and copy/move assignment operators. With respect to the std::vector solution, you're not allocating memory on the heap, since array info leaves on the stack. The downside is that the move operations will cost you O(sizeof(info) / sizeof(ItemType)) instead of O(1).

Also, beware that:

int main() {
    ArrTest<int> ar0{};
    //           ^ This has all its elements initialized to int{} (i.e. (int)0)

    ArrTest<int> ar1;
    //           ^ This has all its elements uninitialized!!

    //ArrTest<int> ar2();
    //             ^ This declares a function!!
}
  • Related