14

If a class has only one constructor with one parameter, how to declare an array? I know that vector is recommended in this case. For example, if I have a class

class Foo{

public:
Foo(int i) {}

}

How to declare an array or a vector which contains 10000 Foo objects?

Eight
  • 4,194
  • 5
  • 30
  • 51
skydoor
  • 25,218
  • 52
  • 147
  • 201
  • 1
    Please don't conclude from the answers that you cannot refer to the array if you don't initialize it right away. You can always do `extern Foo foo[100];` and then already refer to the array, as long as later you define it and *then* it needs all the initializers :) – Johannes Schaub - litb Feb 26 '10 at 18:06
  • Dagnammit, I typed the same comment about `extern`, but thought I ought to check it really works before posting, and you beat me to it. I don't think you even need to define it, as long as you don't reference it. – Steve Jessop Feb 26 '10 at 18:11
  • 1
    Why declare an array and not a vector? – David Thornley Feb 26 '10 at 18:13
  • 2
    I've just had fun with the C++0x GCC, and came up with a mechanism for `std::array`: http://codepad.org/O4bP8KO9 :) I suspect that's the closest one can get - but at least it's a native real array inside. @David, because `vector` dynamically allocates, so it's overkill i think. In C++0x with the codepad code, we may even be able to `constexpr` all involved functions (if we refrain from using reference parameters), and benefit from static initialization to avoid the initialization-order fiasco, i think. – Johannes Schaub - litb Feb 26 '10 at 18:23
  • Note that _declaring_ the array wouldn't be a problem. _Defining_ it is hard. (See here: http://stackoverflow.com/questions/1410563/) – sbi Feb 26 '10 at 18:24

13 Answers13

15

For an array you would have to provide an initializer for each element of the array at the point where you define the array.

For a vector you can provide an instance to copy for each member of the vector.

e.g.

std::vector<Foo> thousand_foos(1000, Foo(42));
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • For plain array, I'm just sayin' you don't *have* to do it that way is all... seems misleading. There are other, practical, alternatives. Plus, the vector approach shown here doesn't allow the constructor to be called with different arguments. – Dan Moulding Feb 26 '10 at 19:56
  • @Dan Moulding: But you can't _define_ an array of a class type with no default constructor without providing initializers. I stand by this statement. The question asks for an array or a vector and doesn't give any guidance as to the values needed so I still don't think that my answer is incorrect or unhelpful even if there are other possibilities. – CB Bailey Feb 26 '10 at 20:11
  • Fair enough. I suppose if you're talking about array *types* that's true. I was thinking more in terms of arrays as a contiguous sequence of objects, in which case... well there are alternatives. – Dan Moulding Feb 26 '10 at 20:32
13

Actually, you can do it as long you use an initialization list, like

Foo foos[4] = { Foo(0),Foo(1),Foo(2),Foo(3) };

however with 10000 objects this is absolutely impractical. I'm not even sure if you were crazy enough to try if the compiler would accept an initialization list this big.

fogo
  • 294
  • 1
  • 7
  • 6
    You can type a little less (if the constructor is not explicit): `Foo foos[] = { 0, 1, 2, 3 };` – David Rodríguez - dribeas Feb 26 '10 at 17:50
  • As you write, that's impractical. However, skydoor asked about array or vector. – sbi Feb 26 '10 at 17:58
  • @David Rodríguez - dribeas Oh sure! I know about that. But for the sake of the example I prefered to be explicit. @sbi I understand your point. Maybe I should have said considering how impratical it is to use a vector instead. – fogo Feb 26 '10 at 18:12
  • This is the way to go on embedded systems without dynamic memory. – Alexander Jan 27 '17 at 13:11
  • Just wanted to point out that if the Foo ctor took multiple arguments, for instance a Foo(int x, int y), then you'd probably have to nest another layer of uniform init: `Foo foos[] = { {0, 0}, {1, 1}, {2, 2} };` – solstice333 Sep 04 '17 at 08:49
8

sbi had the best answer for plain arrays, but didn't give an example. So...

You should use placement new:

char *place = new char [sizeof(Foo) * 10000];
Foo *fooArray = reinterpret_cast<Foo *>(place);
for (unsigned int i = 0; i < 10000; ++i) {
    new (fooArray + i) Foo(i); // Call non-default constructor
}

Keep in mind that when using placement new, you are responsible for calling the objects' destructors -- the compiler won't do it for you:

// In some cleanup code somewhere ...
for (unsigned int i = 0; i < 10000; ++i) {
    fooArray[i].~Foo();
}

// Don't forget to delete the "place"
delete [] reinterpret_cast<char *>(fooArray);

This is about the only time you ever see a legitimate explicit call to a destructor.

NOTE: The first version of this had a subtle bug when deleting the "place". It's important to cast the "place" back to the same type that was newed. In other words, fooArray must be cast back to char * when deleting it. See the comments below for an explanation.

Dan Moulding
  • 211,373
  • 23
  • 97
  • 98
  • 1
    I'm not downvoting as there isn't anything fundamentally wrong with the approach, but you can get the same effect with `std::vector`, `reserve` and `push_back` and it's a lot easier to make it robust and exception safe. – CB Bailey Feb 26 '10 at 21:11
  • 1
    Yup. When I wrote this, I had apparently overlooked the "or vector" at the end of the question, and was focusing entirely on the question as summed up in the title. That being the case, this here is quite possibly the *only* practical way to do it. I certainly wouldn't say this is better than using a vector, but if you *need* a plain array... – Dan Moulding Feb 26 '10 at 21:17
  • There is no need to call the destructors manually. In fact, this is harmful, because `delete[] fooArray` already does that before realeasing the memory. Also, I would replace the first two lines with `Foo* fooArray = reinterpret_cast(operator new(10000 * sizeof(Foo)))`. – fredoverflow Feb 27 '10 at 14:51
  • @FredOverflow: Ooops. You caught a bug. I forgot to cast it back to an array pointer before deleting it. Attempting `delete [] fooArray` would cause the compiler to attempt to destroy `sizeof(foo) * 10000` Foo objects, which is *obviously* not right. It's important to always delete the same type of object(s) that are newed. I newed `sizeof(Foo) * 10000` chars and I must delete `sizeof(Foo) * 10000` chars. This is, of course, why you *do* need to explicitly call the Foo destructors. Fixed. – Dan Moulding Feb 27 '10 at 16:42
  • @sbi: `delete [] place` works too, so long as `place` is still in scope. My example didn't make this very clear, but I was assuming that the cleanup code was in a different context and that therefore, `place` may no longer be in scope (you could of course save "place", but then you must save two pointers instead of just one). – Dan Moulding Mar 01 '10 at 11:59
  • @Dan: I see. The requirement of having to store the pointer is certainly more demanding than just having to know its type. I didn't think of it that way. – sbi Mar 01 '10 at 15:53
4

You'd need to do an array of pointers to Foo.

Foo* myArray[10000];
for (int i = 0; i < 10000; ++i)
    myArray[i] = new Foo(i);
Cory Petosky
  • 12,458
  • 3
  • 39
  • 44
  • 4
    In most cases this would not be a solution (i.e. Foo objects required to be contiguous). This changes the scenario much more than using an `std::vector` in the first case. – David Rodríguez - dribeas Feb 26 '10 at 17:43
  • Agreed. I completely missed the word 'vector' in his final sentence. I thought his 'I know vectors are recommended' was trying to say 'I know vectors are the right solution, but I want an array anyway'. – Cory Petosky Feb 26 '10 at 20:17
3

When you declare an object that has no default constructor, you must initialize it in the declaration.

Foo a; // not allowed
Foo b(0); // OK

The same goes for arrays of such types:

Foo c[2]; // not allowed
Foo d[2] = { 0, 1 }; // OK
Foo e[] = { Foo(0), Foo(1), Foo(2) }; // also OK

In your case, you'll probably find it impractical to initialize all 10,000 elements like that, so you might wish to rethink whether the class really shouldn't have a default constructor.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • 2
    I don't insist, @Andrey. I just don't always remember whether implicit conversions occur in array initializers. Edited to show both syntaxes. The explicit syntax would be required for multi-parameter constructors, but is optional for single-parameter, non-explicit constructors. – Rob Kennedy Feb 26 '10 at 17:50
  • But that's not really an option with the given 10000 objects, is it? – sbi Feb 26 '10 at 17:55
  • The only reason it's not an option, @Sbi, is if the compiler's internal limitations prevent it from compiling an initializer list as complex as one with 10,000 elements. If it's allowed, then it's an option. It's not a *practical* option, but I noted that in my answer already. – Rob Kennedy Feb 26 '10 at 18:09
  • I see. I'm sorry I overlooked that. However, I still don't feel this really answers the question, as that was about array _or_ vector. and there _is_ a practical solution using `std::vector`. – sbi Feb 26 '10 at 18:29
1

You have to use the aggregate initializer, with 10000 of inidividual initializers between the {}

Foo array[10000] = { 1, 2, 3, ..., 10000 };

Of course, specifying 10000 initializers is something from the realm of impossible, but you asked for it yourself. You wanted to declare an array of 10000 objects with no default constructor.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Note that the question was about 10000 objects. That makes for quite a big file to swallow for the compiler. – sbi Feb 26 '10 at 17:56
1

The only way to define an array of a class with no default constructor would be to initialize it right away - not really an option with 10000 objects.

You can, however, allocate enough raw memory whichever way you want and use placement new to create the objects in that memory. But if you want to do this, it's better to use std::vector which does exactly that:

#include <iostream>
#include <vector>

struct foo {
    foo(int) {}
};

int main()
{
    std::vector<foo> v;
    v.resize(10000,foo(42));
    std::cout << v.size() '\n';
    return 0;
}
sbi
  • 219,715
  • 46
  • 258
  • 445
  • *Why* is it better to use `std::vector` if they do the same thing? Because of the 50% increase in code size you get from using STL? ;) – Dan Moulding Feb 26 '10 at 19:18
  • 1
    @Dan: It's better because it avoids common errors. (And that size increase argument is just plain FUD.) – sbi Feb 27 '10 at 10:07
  • Plain FUD? On one embedded system I'm working on, using just *one* instance of a vector made my shared library size jump from ~100KiB to well over 1MiB. That's not FUD. And, in this case, it's an increase that I can't afford. This is with GCC. – Dan Moulding Feb 27 '10 at 16:25
  • @Dan: Well, so you're on an embedded system. Don't get me wrong, but IME most people asking here aren't. For most 1MB isn't an issue, while shipping bug free software certainly is. – sbi Feb 27 '10 at 16:55
  • 2
    @sbi: Well, that's one thing that really irks me. IME software people have a tendency to have a very narrow software world-view. Most think that Linux/Windows/Mac desktop apps and Web development is all there is. It turns out there's actually a *whole lot* of people who do embedded work. It's *not* a niche. – Dan Moulding Feb 28 '10 at 06:24
  • @Dan: I haven't said it's a niche. I said that most people asking questions here are programming for desktop systems. And if they aren't, they usually say that they are working in the embedded field, because they feel it needs mentioning. (In this case, however, skydoor actually even _asked_ about how to do this with a vector.) Anyway, on embedded systems, special rules apply, that many mainstream suggestions don't consider. Heck, on some of them you have to watch the stack size or don't even have free storage. You hadn't suggested that either. – sbi Feb 28 '10 at 08:49
  • @Dan: Here's a nice article on the subject: http://www.joelonsoftware.com/articles/fog0000000020.html – sbi Feb 28 '10 at 11:04
  • 1
    @sbi: That's not a bad article from a desktop software perspective. The irony of that is not lost on me ;) Two things: a) the answers here on SO aren't intended only for the person asking the question, but are also (maybe primarily, even) for the people who come searching for answers *later*. Would it be helpful for an embedded developer who has this question to come here and find answers that are only suitable for desktop environments? b) It's best for library developers to consider *all* the systems on which their code might be useful. Being conservative can be important. +1, BTW :) – Dan Moulding Feb 28 '10 at 19:38
  • I guess my main point is, one isn't necessarily better than the other. As usual, there are tradeoffs. That's all I'm sayin'. – Dan Moulding Feb 28 '10 at 19:40
  • @Dan: I can fully agree to your point that there are tradeoffs. `:)` – sbi Mar 01 '10 at 09:41
1
class single
{
    int data;
public:
    single()
    {
        data = 0;
    }
    single(int i)
    {
        data = i;
    }
};

// in main()
single* obj[10000];
for (unsigned int z = 0; z < 10000; z++) 
{
    obj[z] = new single(10);
}
Rajendra Uppal
  • 19,218
  • 15
  • 59
  • 57
0

Another option might be to use an array of boost::optional<Foo>:

boost::optional<Foo> foos[10]; // No construction takes place
                               // (similar to vector::reserve)

foos[i] = Foo(3); // Actual construction

One caveat is that you'll have to access the elements with pointer syntax:

bar(*foos[2]); // "bar" is a function taking a "Foo"

std::cout << foos[3]->baz(); // "baz" is a member of "Foo"

You must also be careful not to access an unitialized element.

Another caveat is that this not a true replacement for an array of Foo, as you won't be able to pass it to a function that expects the latter.

Manuel
  • 12,749
  • 1
  • 27
  • 35
0

In straight C, using int foo[10000] = {1}; will initialize the first array item to 1 and the remainder of the array to zero. Does C++ not auto-initialize unspecified array members, or does this require a default constructor?

bta
  • 43,959
  • 6
  • 69
  • 99
0

If it makes sense for your class, you could provide a default value for your constructor parameter:

class Foo
{ 
public: 
  explicit Foo(int i = 0); 
}

Now you have a default constructor. (A "default constructor" is a constructor that can be called with no arguments: FAQ)

I'd also recommend making your constructor explicit, as I did above. It will prevent you from getting Foos from ints when you don't want request them.

Bill
  • 14,257
  • 4
  • 43
  • 55
0

Try this.

Foo **ppInstances=0;
    size_t total_instances = 10000;

    for(int parent=0;parent < total_instances;parent++){
        ppInstances[parent]=new Foo( parent ); 
        ppInstances++;
    }
    for(int parent=0;parent < total_instances;parent++){
        delete *ppInstances;
        ppInstances--;
    }

0

The proper way is use to std::aligned_storage. You will have to manually construct and destruct items as well as reintrepret_cast when you want to access an item. I recommend you write a small wrapper class around storage_t to take care of this. Someone mentioned using boost::optional which uses a bool and a storage_t under the hood. This method saves you a bool.

template<typename T>
using storage_t = typename std::aligned_storage<sizeof(T), alignof(T)>::type;

struct Foo;

size_t i = 55;
storage_t<Foo> items[1000]; // array of suitable storage for 1000 T's
new (reintrepret_cast<Foo*>(items + i)) Foo(42); // construct new Foo using placement new
*reintrepret_cast<Foo*>(items + i) = Foo(27);    // assign Foo
reintrepret_cast<Foo*>(items + i)->~Foo()        // call destructor
en4bz
  • 1,092
  • 14
  • 18