32

I want to create in C++ an array of Objects without using STL.

How can I do this?

How could I create array of Object2, which has no argumentless constructor (default constructor)?

5
  • 10
    Out of interest, why don't you want to use stl? Commented Mar 22, 2010 at 15:27
  • @osgx: Why not simply define a default constructor? Commented Mar 22, 2010 at 15:29
  • 4
    @KennyTM That is not a good solution. Many classes cannot and should not be default constructible. Commented Mar 22, 2010 at 15:32
  • 3
    It's a good exercise, you'll deal with explicit memory management and I am delighted that you try to minimize the requirements (no Default Constructor is a sensible constraint imho). Once you're done, and once you've witness all the bugs you went into, you'll be glad that there are libraries already available (STL and Boost). Commented Mar 22, 2010 at 15:47
  • 1
    +1 because on every other hit I checked for "create c++ array of objects" everyone said use vector. std::vector is not always appropriate (although I will agree it's definitely preferable in most cases.) Commented Jan 4, 2013 at 1:57

9 Answers 9

35

If the type in question has an no arguments constructor, use new[]:

Object2* newArray = new Object2[numberOfObjects];

don't forget to call delete[] when you no longer need the array:

delete[] newArray;

If it doesn't have such a constructor use operator new to allocate memory, then call constructors in-place:

//do for each object
::new( addressOfObject ) Object2( parameters );

Again, don't forget to deallocate the array when you no longer need it.

Sign up to request clarification or add additional context in comments.

12 Comments

Object2 has no default c'tor.
@Andrey: That's plain old placement new.
@osgx: void* buffer = ::operator new[]( numberOfObjects * sizeof( Object2 )); That's not an array of objects, that's a block of memory.
@Jeremy: What is the alternative? The objects do not have a default ctor, and he doesn't want to use vector
Placement new is absolutely the right answer. @sharptooth: it starts out as a block of raw memory, it becomes an array of objects when the constructors are called. But be sure to handle exceptions properly and call destructors manually and only on those objects which finished construction. It isn't trivial.
|
13

Assuming that your class is Base and you have a one argument constructor

Base arr[3] = {Base(0), Base(1), Base(2)} ;

2 Comments

This is the best solution if you know the size of the array at compile time, if not go with vector really.
I know the size at container consruction time only.
13
// allocate memory
Object2* objArray = static_cast<Object2*>( ::operator new ( sizeof Object2 * NUM_OF_OBJS ) );
// invoke constuctors
for ( size_t i = 0; i < NUM_OF_OBJS; i++ )
  new (&objArray[i]) Object2( /* initializers */ );

// ... do some work

// invoke destructors
for ( size_t i = 0; i < NUM_OF_OBJS; i++ )
  objArray[i].~Object2();

// deallocate memory
::operator delete ( objArray );

6 Comments

Does static_cast work like that? (I always figured you needed reinterpret_cast to get from void* to anything else, but honestly have never tried static_cast.) ...and you forgot to destroy your objects before deleting the array, you need to explicitly call the dtor: for (...) objArray[i].~Object2();
Why we need here a static_cast not the simple (Object2*) one?
@osgx: Using C++ style casts rather than a C style cast means that you always get the type of conversion that you ask for. If you make an error then you get a copmile error rather than falling back to a potentially more dangerous conversion that a C style cast would give you.
@dash-tom-bang, thanx, I completely forgot about destructors :) static_cast is more safe here because it can't cast Object2* to SomeThingElse* where reinterpret_cast (or C-style cast) can.
Explicitly calling destructors? Are you sure this isn't ... you know ... evil?
|
5
Object2 *myArray[42];
for (int i = 0; i < 42; i++)
{
  myArray[i] = new Object2(param1, param2, ...);
}

Later on you will have to walk through the array and deallocate each member individually:

for (int j = 0; j < 42; j++)
{
  delete myArray[j];
}

3 Comments

Object2 myArray[42]; this step won't work unless Object2 has a default constructor (user-declared or otherwise).
@Charles Bailey. Yup, I noticed that a second after I posted it, and changed it to an array of pointers.
heeey. It is not an array of objects, is it array of pointers!
3

Use an array of pointers to Object2:

std::tr1::shared_ptr<Object2>* newArray = new shared_ptr<Object2>[numberOfObjects];
for(int i = 0; i < numberOfObjects; i++)
{
    newArray[i] = shared_ptr<Object2>(new Object2(params));
}

Or, alternatively, without the use of shared_ptr:

Object2** newArray = new Object2*[numberOfObjects];
for(int i = 0; i < numberOfObjects; i++)
{
    newArray[i] = new Object2(params);
}

5 Comments

As others point out, this adds a lot of memory usage overhead (uses a bunch of heap blocks, each of which costs a heap header) and breaks spatial locality.
@Ben Voigt, what do you mean "bunch of heap blocks"? How long is heap header and is it necessary for each new Object2(params)?
Size of heap header varies with compiler settings and whether the debug heap is in use or not, but for the implementations I'm familiar with it is a minimum of 2*sizeof(void*), which is 8 bytes for a 32-bit process. Every call to ::new, ::new[], or malloc has to include space for the heap header inside the heap block it returns, and the return address is pretty much guaranteed to be a multiple of sizeof(void*). It would not be unusual for new char() to actually allocate 64 bytes or more in a debug build, which has checks for underflow, overflow, records line doing allocation, etc.
@Ben Voigt, I get it. Thanks!. What about next thing? With using pools of constant length allocations (e.g. array_of_16byte_objects), heap will increment counter of objects_count_of_16byte, and mark the place as filled in bitmap. Do you know any such implementation?
There are alternate allocators out there which use pools as you suggest (but what happens when the pool runs out, or the only "free" memory is in the pool and some other sized object needs to be allocated). And you can actually redefine "new" for your class to use the pool. But it's pointless to track usage of individual pool slots when you wanted a big array in the first place. Just using one big buffer along with placement new is more efficient than the pool. Now if you needed a linked list or something else that could grow, the pool might be better after all. But not for an array.
3

You can do what std::vector does and create a block of raw memory. You then construct your objects which don't have a default constructor in that memory using placement new, as they are required. But of course, if you do that you might as well have used std::vector in the first place.

Comments

0

If no default constructor is available, you will need an array of pointers, and then loop over that array to initialize each of the pointers.

1 Comment

That's unlikely to be fast: allocation issues and cache misses are likely.
0

The obvious question is why you don't want to use the STL.

Assuming you have a reason, you would create an array of objects with something like Obj * op = new Obj[4];. Just remember to get rid of it with delete [] op;.

You can't do that with an object with no constructor that doesn't take arguments. In that case, I think the best you could do is allocate some memory and use placement new. It isn't as straightforward as the other methods.

1 Comment

I'm trying to implement stl-like structures from scratch. It will be not a vector, but smth like B-tree of constant-length vectors. And I want full control, so I want to implement it entire by myself.
0

If you genuinely need an array (contiguous sequence of objects) of a non-default constructible type and for some reason you cannoy user std::vector (!?) then you have to use a raw allocation function and placement new.

This is very hard to do reliably; this should help to show why. This snippet includes some defence against exceptions but is more than likely not robust against all failures.

const size_t required_count = 100; //e.g.

// cast to pointer of required type needed for pointer arithmetic
Object2* objarray = static_cast<Object2*>(operator new(required_count * sizeof(Object2)));

size_t construction_count = 0;

try
{
    while (construction_count < required_count)
    {
        // params could change with index.
        new (static_cast<void*>(objarray + construction_count)) Object2(param1, param2);
        ++construction_count;
    }
}
catch (...)
{
    while (construction_count-- != 0)
    {
        try
        {
            (&objarray[construction_count])->~Object2();
        }
        catch (...)
        {
            // not a lot we can do here, log but don't re-throw.
        }
    }

    operator delete(objarray);
    throw;
}

// Now objarray has been allocated and pointer to an array of required_count Object2
// It cannot be de-allocated via delete[] or delete; you must loop through
// calling destructors and then call operator delete on the buffer.

4 Comments

There are many good reasons to avoid STL, and those who avoid STL also tend to avoid exceptions. ;)
Can I do a construction of object without placement new, but using objarray[i]->Object2(param1,param2) ?
@dash-tom-bang: I'm attempting to be as exception neutral as possible, only protecting against exceptions thrown by memory allocation failure and the client object. I'm not throwing any new exceptions that other solutions aren't; they are (in general) just ignoring potential exceptions that I'm not.
@osgx: No. placement new is the only way to construct an object without also allocating memory in the same statement. You can't call a constructor on an existing object which is what your suggested syntax is implying.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.