1

I want to make a dynamic array of foo, with the number of items being x. Arguments y and z are to be passed to the constructor of the item foo. I was hoping to do something similar to:

Foo* bar = new Foo(y, z)[x];

However that produced the following compiler error:

 error: expected `;' before '[' token

So after speaking with an experienced friend, he gave me this, which he admitted was a lazy way of doing it, but it works. I was wondering, is there a better/proper way?

Foo* bar = (Foo*) new int[x];
for (int i = 0; i < x; i++) {
    bar[i] = Foo(y, z);
}
hiddensunset4
  • 5,825
  • 3
  • 39
  • 61
  • 6
    You and your friend should get a [good book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) to get a good base of understanding. I mean that second code block is just wrong, why allocate `int` and then cast to `foo`? That's almost certainly undefined behavior. – GManNickG Sep 02 '10 at 17:25

4 Answers4

11

"I want to make a dynamic array" So use a std::vector, it exists for a reason.

std::vector<foo> bar(x, foo(y, z));

This creates a dynamic array with x elements initialized to foo(y, z).


The above makes copies of the second parameter, x times. If you want to generate values for the vector, use generate_n:

std::vector<double> weights;
std::generate_n(std::back_inserter(weights), x, ...);

You replace ... with a function or functor to call, that returns a value. Generally you make a functor:

struct generate_weight
{
    double operator()() const
    {
        return random(1e-3);
    }
};

Giving:

std::generate_n(std::back_inserter(weights), x, generate_weight());

If your compiler supports C++0x, you can take advantage of lambda's. These do the same thing, except they keep the code concise and localized:

std::generate_n(std::back_inserter(weights), x,
                [](){ return random(1e-3); } );
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Whilst this works fine, in this example it returns the same number for all items in the array: std::vector weights(x, random(1e-3)); – hiddensunset4 Sep 02 '10 at 17:41
  • @Daniel: Indeed it does, it initializes it to `x` copies of `foo(y, z)`. Your question said nothing about anything else; ask real questions to get real answers. I've updated my answer. – GManNickG Sep 02 '10 at 17:55
  • I was just looking to see if that was the expected behavior, having no experience with STL before this. I used the random(1e-3) as an example. Thanks for the extended answer. – hiddensunset4 Sep 02 '10 at 18:12
  • @Daniel: No problem. Programming C++ without using the standard library is...unwise. – GManNickG Sep 02 '10 at 18:31
1

If you want to initialize each element to the same value, then do as GMan suggested above:

std::vector<foo> bar(x, foo(y, z));

You will have a vector of X elements, each with the same foo(y,z);

If you want a unique value for each of the foos you will need to initialize it in a loop. one simple example of how is:

std::vector<foo> bar;

    for (int i = 0; i < x; ++i)
    {
        // initialize X foos with different values for each foo.
        bar.push_back(foo(i, random(i)));
    }
Lima Beans
  • 292
  • 2
  • 11
1

Firstly, the only initializer you can use in the array new-expressions is (). So, these are you only options if you want to use new-expression

foo *bar = new foo[x];   // 1. no initializer
foo *bar = new foo[x](); // 2. `()` initializer

Depending on how foo is defined, these two might behave identically or differently.

Secondly, since you can't pass the constructor arguments (y,z) in new-expression, you have to default-construct your array (as shown above) and then use assignment to give your array elements specific values. For this your foo has to be default-constructible and copy-assignable. The code will look pretty much like what your "experienced friend" suggested, but with proper types (instead of int). What you have now is useless (where did that int come from?). Most likely you misunderstood your experienced friend. This is how it is supposed to look

foo *bar = new foo[x];
for (int i = 0; i < x; i++)
  bar[i] = foo(y,z);

Thirdly, in your specific case std::vector will do the same thing in a much more elegant way.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • That was my original code, however I would have to make a constructor to handle no arguments (as you said, 'default constructible'?) when using "foo *bar = new foo[x];". And then re-init them all as you have done. – hiddensunset4 Sep 02 '10 at 18:11
0

If you insist in not using std at least do it properly. This is not safe and has BIG alignment issues.

size_t x = 10;
foo * bar= static_cast<foo*> (::operator new ( sizeof(foo)* x));
for (size_t i=0; i<x; ++i)
{
    new (&bar[i]) foo (1,1); 
}

::operator delete (bar); 
Charles Beattie
  • 5,739
  • 1
  • 29
  • 32
  • 1
    There's no alignment issues here, `operator new` returns a maximally aligned block of memory. – GManNickG Sep 02 '10 at 17:58
  • @GMan - I'm afraid not this will ignore alignment of foo. – Charles Beattie Sep 02 '10 at 18:13
  • 1
    Again, there are no alignment issues. §3.7.3.1/2: "...The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type and then used to access the object or array in the storage allocated..." – GManNickG Sep 02 '10 at 18:32