-4

As many other questions and answers already stated, there is no syntax in C++ which allows you to declare and fill a dynamic-sized array with non-default constructible objects.

Obj* array = new Obj[size];

Here, if Obj has a default constructor, it will be used to fill array with size default-constructed instances of Obj, which is a problem.
The most recurrent answers to this question mention vectors (here), or the mechanism used by vectors, the placement new (here). However, the former is not an option in my case, and I would like to avoid the latter because it looks plain dirty and messy to me (being used by the STL perhaps makes it a nice way to do things, but it really does look messy).
Edit: why vectors are not an option: This project is a challenge I want to test myself against, and I want to get my hands in the dirt as much as I can. If not using vectors means using placement news as stated below, then that is what I will do.
I do realise that I complained about placement news being messy, and I also do realise that getting hands in the dirt does imply having to handle such mess.

Another recurrent answer is to use the curly braces to fill the array with non-default constructed objects:

Obj* array = new Obj[2] {Obj(foo), Obj(bar)};

That makes it a half-dynamic-sized array, if I may say. Being allocated on the heap makes it not static, but the size has to be a compile-time constant, so I would not consider it fully dynamic (or at least not as much as I want it to be).

The most obvious solution to me would be to declare the array as above, let it be filled with junk, default-constructed Objects, and then re-fill it up with the correct objects, as follows:

Obj* array = new Obj[size];
for (int i = 0; i < size; i++)
{
    array[i] = Obj(whatever);
}

However, performance is a great deal in my program, and I am quite concerned about the performance impact of such methods. If size is 10000, the array would be filled with 10000 junk objects, which could be very time-expensive. Then, the cost of the replacement afterwards could be even worse (while still possible to minimise with efficient use of the copy-and-swap idiom), and that too is a concern.
Instead, I was thinking of using a double malloc.

Obj** array = malloc(sizeof(Obj*) * size);
for (int i = 0; i < size; i++)
{
    array[i] = malloc(sizeof(Obj));
    array[i] = new Obj(whatever);
}
//...
for (int i = 0; i < size; i++)
{
    delete array[i];
    free(array[i]);
}
free(array);

But that looks totally not cache-friendly. And it just does not feel right having a pair of malloc (having only one already feels not okay).

So here is my question: is there a nice and clean way that allows you to allocate uninitialised memory, and then fill it with custom-constructed objects, that does not degrade performance?
Edit: my real question is: are there other ways than the ones I mentioned above?

(P.S.: I know that "uninitialised memory" does not go well with RAII, and thus does not go well with "nice and clean")

  • 2
    The answer still is uninitialized memory + placement new. All your alternatives are broken one way or the other. The ugliness only has to live in your container implementation, nicely encapsulated in a class. Your main code would not be affected. – amon Jan 8 '17 at 8:55
  • 3
    Your question is self-contradictory. You essentially say, "I know the proper way to do this is vector, or failing that placement new, but I can't use the former for unspecified reasons, and don't want to use the latter because it fails my personal sense of aesthetics. What is the proper way to do it?" You already know the proper way. You're looking for yet another one. – Sebastian Redl Jan 8 '17 at 11:49
  • 3
    Why is vector not an option for you? And what about a custom collection class with similar properties to a vector? – CodesInChaos Jan 8 '17 at 12:02
  • @amon I see, so that's the only option I have left. – qreon Jan 8 '17 at 18:57
  • @SebastianRedl I know my question is self-contradictory, I even stated it myself (though I may have sounded bold or harsh). And yes, I was looking for other ways to implement this, ways that I might not have been aware of. (at this point, I can clearly see that my question was asked the wrong way and I understand the downvotes). As for why vectors are not an option for me: – qreon Jan 8 '17 at 19:01
2

Given what you've described in the comments (wanting to create a 2-dimensional array), there is an alternative that avoids both explicitly using placement new to create objects into previously "raw" memory, and the terrible cache usage of an array of arrays model (implemented either as an array of pointers to separately allocate memory blocks, or a std::vector<std::vector<T>> sort of thing.

That alternative is to allocate a single block of memory for the entire rectangular array, and do a little bit of operator overloading to get 2-D addressing into that block.

A simple version of this looks something like this:

class array2D { 
    std::vector<double> data;
    size_t columns;
public:
    array2D(size_t x, size_t y) : columns(x), data(x*y) {}

    double &operator(size_t x, size_t y) {
       return data[y*columns+x];
    }
};

This is "dynamic" only to the extent that it allows you to specify the size of the array when you create it, but (as it stands right now) you can't change the size after creation.

If you want to write your own imitation of std::vector as well, that's always entirely possible--but it's a little tricky in a few respects. I wrote a blog post a few years ago that covers at least a few of the basic points, and @Loki Astari wrote a rather more extensive series about it, complete with a Code Review of his implementation.

1

is there a nice and clean way that allows you to allocate uninitialised memory, and then fill it with custom-constructed objects, that does not degrade performance?

Use std::vector, reserve() and emplace_back().

Why reinvent the wheel if the standard library has solved your problem already!

However, the former [using vector] is not an option in my case,

Why is it not an option?

If some manager set some rule to prohibit vector, try to change that rule. Or find some open source implementation of std::vector and rename it to not_std::not_a_vector.

Because if you solve this problem properly, you will end up not only having reinvented std::vector, but you will have lost a lot of time as well.

  • 2
    Didn't the OP write "the former [using vectors] is not an option in my case"? Of course, it would have been helpful if he had told us why it is not an option. – Doc Brown Jan 8 '17 at 11:02
  • 1
    @DocBrown I must have missed that line. Nevertheless, any solution using unitialized memory, placement new and a RAII wrapper to avoid memory leaks will end up as a reinvention of std::vector. So I agree with you: it would be helpful if he had told us why it is not an option. – Sjoerd Jan 8 '17 at 11:05
  • 1
    Yeah, he pretty much has to use std::vector. The only question is whether or not it is remotely as performant, sane, generic, and correct as the Standard version. – DeadMG Jan 8 '17 at 13:02
  • Copy/paste of the comment under my question: vector is not an option for me, for essentially two reasons. The class I am implementing is a Matrix class, which basically is an array of MatrixRows, which itself is an array of doubles. If I use a vector in one of these classes, it makes no sense not to use a vector for the other one too. And yet, having a vector of vectors feels strange. That is the first reason. The second reason is that this project is a challenge I decided to test myself against. I want to do as much code as I can, get my hands in the dirt. – qreon Jan 8 '17 at 19:16
  • So basically, I would like to avoid using vectors just as much as I would like to avoid relying on an already implemented Matrix class in a well renowned library such as Eigen. That is why I am trying to do everything by myself. However, I did not find useful to mention this in my question (whose real purpose was to ask for other ways than the ones I mentioned (yes, it was asked in a bad way, and I understand the downvotes)). Now that I know I will have to resort to placement new to achieve what I want, that is what I am going to do. Thank you for your help :) – qreon Jan 8 '17 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.