How do I create a class constructor that initializes arrays of objects

Started by
10 comments, last by Zahlman 16 years, 10 months ago
I have a class, EnemyShip, and I want to make an array of EnemyShip called smallShip like so: EnemyShip smallShip[3] But my compiler (MVSC++ EE) won’t allow that because “'EnemyShip' no default constructor to initialize arrays of objects.” So I tried to make a constructor for arrays like so: EnemyShip[] but my compiler won’t accept it because “empty attribute block is not allowed” I searched the forums and Google but I couldn’t find anything relevant. Please help. Thank you in advance.
Advertisement
I'm assuming you are receiving Error C2539. All that is needed to create an array of objects is a default constructor (a constructor which takes no parameters). If you have defined a constructor for your EnemyShip object that takes parameters then the compiler will not generate a default one for you so you also need to define a default constructor.

From MSDN (http://msdn2.microsoft.com/en-us/library/41bws1hs(VS.80).aspx):
"new : 'class' : no default constructor to initialize arrays of objects

Initializing an array of objects requires a default constructor (a constructor with no parameters), which is called separately for each object in the array. The constructor was not available. If any constructor is defined, the compiler does not generate a default constructor and you must supply one."
Is this what you are trying to do?

// allocate an array of 3 EnemyShipsEnemyShip* smallShip = new EnemyShip[3];


If it is, be sure to clean up the dynamically allocated memory when your done with those ships (like when you exit your app).

// free the memorydelete [] (smallShip);

Wow thank you so much theNOOB it compiles now.

Haiduk67, I'm not allocating memory for them (new and delete don't like me) but thank you very much for replying.
Or, just define a default constructor, if you've already defined your own constructor that accepts parameters. Something like this:
class Ship{   private:    .    .    .   public:    Ship( your constructor parameters here )    Ship();   <---- Your default constructor};


This would allow you to do what you want, but then you'll have to add some functions that allow you to set the class members for each Ship once you've created your array.
Ok I have another question now. How do I make my inherited class "inherit" the deconstructor from the base class?

I know how to "inherit" the constructor:
derived_constructor_name (parameters) : base_constructor_name (parameters) {...}

but that doesn't work with the included "~" for the deconstructor.


Please help and thank you again for replying last time.
Make sure that you are declaring your destructor as virtual if you plan to inherit from it.

Ex.
class EnemyShip{public:EnemyShip();virtual ~EnemyShip(); //<--This should be virtual if you want to inherit;//...};


That should solve ur problem...
Yeah, you don't need to explicitly call your base class's destructor like you do the constructor - it'll get called for you automatically. The only catch is what TheN00B described - make the base class's constructor virtual - it's a good idea to do that or you might fall foul of one of the details of the way C++ works (specifically, when you 'delete x' it figures out which destructors to call by looking at the type of x, which might not actually be the type of the object it points to).

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

OK, we need to clear up a few things here.

0) To make sure the previous stuff was fully understood: when the compiler says "'EnemyShip' no default constructor to initialize arrays of objects.", what that *means* is "because 'EnemyShip' does not have a default constructor, I can't make an array of them. You asked for an array, so I have to complain now." This is because, except in very limited circumstances*, each element of an array needs to be default-constructed.

* If the elements are not an object type, then of course they aren't default-constructed because there are no constructors. Also, we can use the brace-initialization syntax for arrays that are local variables, but *not* for members of a class, because it's an *initialization* syntax, and it's incompatible with the syntax of the initialization list.

So all you need is a default constructor of some sort. There is no such thing as a "constructor for arrays". Objects normally do not care about their containers, and actually it is generally a bad idea to make them aware of their containers.

Quote:Original post by Evil Booger
Ok I have another question now. How do I make my inherited class "inherit" the deconstructor from the base class?

I know how to "inherit" the constructor:
derived_constructor_name (parameters) : base_constructor_name (parameters) {...}


First off, the term is "destructor", and not "deconstructor".

Next, this isn't really inheriting a constructor; it's initializing the "base part of" the derived object.

When an object is destructed, its members get destructed first, *automatically*. The reason you (sometimes) need to mention bases (base-parts-of) in a constructor initializer list is that there are multiple constructors to choose from, and the compiler can't choose for you. But because destructors never accept an argument, there can only be one for any class, which means there can only be one for each member (including bases)'s type, which means there is no decision to be made - the compiler just calls the appropriate destructors.

BUT, you will sometimes need to declare the base class' destructor to be 'virtual'. The reason for this is to make it behave polymorphically: if you 'delete' a pointer-to-Base that is actually pointing at a Derived object, and the destructor is not 'virtual', then no lookup is done. This means that the code never checks, at run-time, to figure out that the pointed-at thing is actually a Derived object. Which means that the wrong destructor gets called: i.e. the whole Derived object gets passed just to the code that is intended to destruct Base objects. This is NOT the same as "the Base part of the Derived object gets destructed, and the rest of the Derived object is left alone", and therefore it is NOT OK to do this when the rest of the Derived members don't have any special cleanup. It is undefined behaviour.

The rule of thumb is, if *any other member function* of the class will be virtual, ensure the destructor is, too. If that ever doesn't work for you, it's probably because you're doing something really strange that should be reworked anyway. This does mean that sometimes, you will declare and define an empty, virtual destructor in a base class (because you have to mark it, and in order to mark it you have to declare it; and since you declared it, the default one isn't generated, so you have to provide a definition too), and not override it in any derived classes. This is much better than having things seem to work for most of the project development cycle and then suddenly and mysteriously blow up before an important deadline. Undefined behaviour is kinda evil like that, you know.

When there are *no* "plain" virtual functions, though (and note that constructors, including copy constructors, and the assignment operator cannot be made virtual), there is no need to mark the destructor that way - and in fact, doing so can only hurt performance. With normal implementations, there is a once-per-class cost in object size that you pay for having virtual functions (including a destructor) - making one virtual is the same as making them all virtual.
Quote:The rule of thumb is, if *any other member function* of the class will be virtual, ensure the destructor is, too. If that ever doesn't work for you, it's probably because you're doing something really strange that should be reworked anyway.


Let's not forget:
http://en.wikipedia.org/wiki/Rule_of_three_(C++_programming)
and the assignment operator, copy constructor and self-assignment.

I mention this because of possible dynamic allocation.

Also, you're storing objects, not pointers to object. So as you pass objects around, there's a possibility that your EnemyShip objects will get copied, and, if they do some funny stuff, can possible multiply themselves within some other system.

Overall, there's really a lot of tiny details that you'll need to cover to make solid class.

All of which is conveniently covered here: http://www.parashift.com/c++-faq-lite/index.html

This topic is closed to new replies.

Advertisement