views:

178

answers:

1

I am trying to create a class which has a private member that is an array. I do not know the size of the array and will not until the value is passed into the constructor. What is the best way to go about defining the class constructor as well as the definition in the .h file to allow for this variable size of the array?

+7  A: 

If you want a "real" C-style array, you have to add a pointer private member to your class, and allocate dynamically the memory for it in the constructor (with new). Obviously you must not forget to free it in the destructor.

class YourClass
{
  private:
    int * array;
    size_t size;

    // Private copy constructor/assignment operator to block copying of the object, see later
    YourClass(const YourClass & Other) {}
    YourClass& operator=(const YourClass& Other) {}

  public:
    YourClass(size_t Size) : array(new int[Size]), size(Size)
    {
        // do extra init stuff here
    };

    ~YourClass()
    {
        delete [] array;
    }
};

To make this work easier, you may consider to use a smart pointer (for example, a boost::scoped_array), that you may initialize using the initializer list before the constructor or simply in the constructor.

class YourClass
{
  private:
    boost::scoped_array<int> array;
    size_t size;
  public:
    YourClass(size_t Size) : array(new int[Size]), size(Size)
    {
        // do extra init stuff here
    }

    // No need for a destructor, the scoped_array does the magic
};

Both these solutions produce noncopyable objects (you didn't specify if they had to be copyable and their copy semantic); if the class don't have to be copied (which happens most of times), these both are ok, and the compiler will generate an error if you try to copy/assign one class to another, in the first case because the default copy constructor and assignment operator have been overloaded with private ones, in the second case because boost::scoped_array is noncopiable.
If, instead, you want to have copyable objects, then you must decide if you want to create a copy that shares the array (so, just a pointer copy) or if you want to create a new, separate array for the other object. In the first case, you must be very careful before freeing the allocated memory, since other objects may be using it; a reference-counter is the most common solution. You can be helped in this by boost::shared_array, which does all the tracking work automatically for you.
If instead you want to do a "deep copy", you'll have to allocate the new memory and copy all the objects of the source array to the target array. In the operator= you have to remember to free the old array of the destination object (Other in the code). Moreover, in operator= you must also take extra care to check if &Other == this, and, in this case, avoid the copy.

Still, the simplest solution is to use a std::vector as a private member: it would handle all the allocation/deallocation stuff by itself, constructing/destroying itself correctly when the object of your class is constructed/destructed. Moreover, it implements the deep-copy semantic out of the box. If you need to make your callers access the vector read-only, then, you could write a getter that returns a const_iterator or a const reference to the vector object.

Matteo Italia
For arrays, you'll want to make sure to use `scoped_array`, not `scoped_ptr`.
James McNellis
Corrected, thank you. I always get the delete [] right, but sometimes I forgot about the array versions of the smart pointers.
Matteo Italia
The only sniggle is if YourClass is going to be copyable, because scoped_* is noncopyable in boost. That is not specified in the question anyway, so +1 anyhow.
Skurmedel
For the second `YourClass` it is already taken care of by `scoped_array` (which is noncopyable). But for the first one, you either have to implement copy constructor and assignment, or disable them (make private). It's too easy to accidentally copy an object, and then end up with a stale pointer.
Johannes Schaub - litb
@Skurmedel: Well, in that case the thing gets more complicated, because you have to define the copy semantic of the class; in most cases, however, if the copy is not used/needed it's better to just leave it noncopyable.@Johannes Schaub: I wanted to keep the example simple, but since it's a common flaw, I'll correct it.
Matteo Italia
Ok, corrected, please check if I made some mistake. :P
Matteo Italia