Array question


#1

Is it OK to use the Array.add() function with temporaries or local (stack) variables? Here is some code, which creates an Array of 100 bools, all set to the value "false":

class Foo
{
    void fillArray()
    {
        for (int i = 0; i < 100; i++)
            myBoolArray.add(false);
    }

    Array<bool> myBoolArray;
};

The code does compile and runs in VS 2013. But is it OK? I am asking, because I saw that Array.add() is using move semantics. And I am still unsure about move semantics. I mean: the temporary value "false" is probably created on the stack, right? So if we move it into the Array, what happens if the fillArray() function finishes? Are the values inside of the Array still valid?

 


#2

Move semantics and the whole "copy as value vs. copy as reference" stuff only apply to objects of a (complex) class. A simple bool literal can be added like this without problems.


#3

Ah, I see.

What would happen, if I did the same with an object instead of a bool. Would still work, I guess?

 


#4

If you look at the implementation of Array add it is something like this:

    void add (const ElementType& newElement)

    {

        const ScopedLockType lock (getLock());

        data.ensureAllocatedSize (numUsed + 1);

        new (data.elements + numUsed++) ElementType (newElement);

    }

The first line is about being able to be thread safe with a CriticalSection, etc. The second line is about allocating in larger blocks. The last line is where the action occurs. It is using a "copy constructor" to add the new item. So it is copying the stack object by reference to the Array.

In concept, this should work fine - and often will, particularly with other Juce classes. The problem is when you start making arrays of your own complex objects. The reason that Juce has a NOCOPY macro is that the default copy operator generated by C++ is pretty dumb. So it is pretty easy to define objects that don't copy correctly. 

So if you are going to use Array this way, make sure that your objects have a proper copy constructor and copy operator. An alternate approach would be to used OwnedArray. It is always an array of POINTERS to your objects. So the inside of your loop would be something like:

myOwnedArray.add (new MyComplexObject());

It is RAII, so OwnedArray will take care of delete for you. It is not that Array is bad, but for big and/or complicated objects, OwnedArray will stress the stack less and avoid a lot of copy overhead.