19

I'm currently working on a card game, and I'm having trouble with some initialization code:

// in my class...
Card cards[20];
// in method...
for(int i = 0; i <= 20;i++)
    cards++ = new Card(i, /*i as char +*/ "_Card.bmp");

The trouble is that my compiler's telling me that cards++ is not an l-value. I've read up on the whole pointer-array equivalence thing, and I thought I understood it, but alas, I can't get it to work. My understanding is that since cards degrades to a pointer, and the new operator gives me a pointer to the location of my new instance of Card, then the above code should compile. Right?

I've tried using a subscript as well, but isn't cards+i, cards++, and cards[i] just 3 ways of saying the same thing? I thought that each of those were l-values and are treated as pointers.

Robᵩ
  • 163,533
  • 20
  • 239
  • 308
Stephen Collins
  • 3,523
  • 8
  • 40
  • 61
  • 1
    cards+i returns cards+i; cards++ increments cards by one; cards[i] returns a reference to the i'th element in cards. They are all different. –  Sep 26 '11 at 16:44

5 Answers5

25
Card cards[20];

cards is already an array of objects. They are constructed with the default constructor(constructor with no arguments). There is no need to new again. Probably you need a member function equivalent to constructor arguments and assign through it.

for ( int i=0; i<20; ++i ) // array index shouldn't include 20
   cards[i].memberFunction(/*....*/);

Even simpler is to use std::vector

std::vector<Card> cards;
for( int i=0; i<20; ++i )
    cards.push_back(Card(i, /*i as char +*/ "_Card.bmp"); )
Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • I figured a vector would be a better idea. So I tried using a vector, but now I'm getting something I have been running into a alot: Once I included , I get a list of insane-looking "unresolved externals" errors from libcpmtd.lib. This tells me there's nothing wrong with MY code, but I still won't compile... Ugh. – Stephen Collins Sep 26 '11 at 16:54
  • 1
    I you wish to use std::vector and now the number of element to be added, DO NOT USE push_back: The overhead is small but avoidable. You should initialize the vector at the right size 'std::vector cards(20);' and then initialize the members the same way you did for the Array. – Clodéric Sep 27 '11 at 08:48
  • 2
    @Clodéric You could also address the inefficiency of `push_back` by simply pre-allocating the vector: `std::vector cards; cards.reserve(20);` This enables calling `emplace_back` with your custom constructor arguments via perfect forwarding: `cards.emplace_back(i, /*i as char +*/ "_Card.bmp");`. This seems more readable/maintainable since it uses constructors instead of mandatory initialization methods. – Jeff G Apr 29 '19 at 17:21
5

The code Card cards[20]; already creates an array of 20 Card objects and creates them with the default constructor. This may not be what you want given your code.

I would suggest using vector instead.

std::vector<Card> cards;

for(int i = 0; i < 20;i++)
{
    cards.push_back(Card(i, /*i as char +*/ "_Card.bmp"));
}

Note that your for loop goes from 0 to 20 and thus one past the end of the array.

Mark B
  • 95,107
  • 10
  • 109
  • 188
4

If you want to avoid unnecessary constructor calls and unnecessary resizing, then it's more complicated, because C++ normally initialises each objects one-by-one as it's allocated. One workaround is to do it the Java way -- use a loop and an array of pointers, like so:

Card *cards[20];
for (int i=0; i<20; i++) {
  cards[i] = new Card(i);
}

Another option is to use malloc to get explicitly uninitialized memory:

Card *cards = malloc(20 * sizeof(Card));
for (int i=0; i<20; i++) {
  new (&(cards[i])) Card(i);
}
Pr0methean
  • 303
  • 4
  • 14
4

Well, there is another possibility, when you are ok with your constructors being called automatically at initialization:

// in my class...
Card cards[20] = { Card(0, "0_Card.bmp"), Card(1, "1_Card.bmp"), /* ... */ };

The huge downside is that you cannot use a loop in this case.

Iziminza
  • 374
  • 3
  • 8
1

An array name, cards in your code, contains the address of the first element of the array. Such addresses are allocated at run time and you cannot change them. Hence the compiler complaining about cards being not an l-value.

But you can definitely specify what those addresses can hold by using a pointer like below:

// in my class...
Card cards[20];

Card *cardsPointer = cards;// Pointer contains the address of the
//1st element of 'cards' array.

// in method...
for(int i = 0; i < 20; i++)
*(cardsPointer++) = Card(i, /*i as char +*/ "_Card.bmp");// Note that 
// there is no 'new' operator as 'cardsPointer' has type 'Card *' and 
// not 'Card **'. And 'cardsPointer' has type 'Card *' as the array is
// of type 'Card'.
Vaishak Nair
  • 898
  • 10
  • 13