So I first learned Java and now I'm trying to switch over to C++. I'm having a little difficulty getting arrays to work correctly.
Right now I am simply trying to create an array of object "Player" and populate it with one. But I get an error.
Player* players = new Player[1];
players[0] = new Player(playerWidth, playerHeight, 20, 1);
The error says: the operand "=" matches these operands. Operand types are: Player = Player *
I can't understand why this doesn't work?
Objects represent a special data type that is mutable and can be used to store a collection of data (rather than just a single value). Arrays are a special type of variable that is also mutable and can also be used to store a list of values.
An array-like is an object. Has indexed access to the elements and a non-negative length property to know the number of elements in it. These are the only similarities it has with an array. Doesn't have any of the Array methods like push, pop, join, map, etc.
Arrays are best to use when the elements are numbers. objects are best to use when the elements strings (text).
Array of Objects in c++ The array of type class contains the objects of the class as its individual elements. Thus, an array of a class type is also known as an array of objects. An array of objects is declared in the same way as an array of any built-in data type.
What the error is saying is that you are trying to assign a value of the wrong type to the variable. When the error says Player = Player *
that means that the variable on the left hand side is a Player
and the value on the right hand side is a Player *
.
players[0] = new Player(playerWidth, playerHeight, 20, 1);
The problem is similar to if you were to do:
int x;
x = "Hello, World!";
The left and right hand types don't match, and there's no natural conversion, so you get an error.
The first problem is that you're coming from a Java background, and Java uses pointers a lot but hides them from you. C++ doesn't hide them at all. The consequence is that C++ has different syntax for explicitly dealing with pointers. Java got rid of all that and mostly used the regular, non-pointer syntax from C++ for dealing with pointers.
Java: C++:
Player player = new Player(); Player *player = new Player();
Player player2; Player *player2 = nullptr;
** no equivalent in java ** Player player3;
player.foo(); player->foo();
** no equivalent in java ** player3.foo();
** no equivalent in java ** *player;
** no equivalent in java ** &player2;
It's very important to understand the difference between working with pointers and working directly with an object:
Java: C++:
Player a = new Player(); Player *a = new Player();
Player b = a; Player *b = a;
b.foo(); b->foo();
In this code there's only a single object, and you can access it through either a
or b
and it doesn't make a difference, a
and b
are both pointers to the same object.
C++:
Player c = Player();
Player d = c;
d.foo();
In this code there are two objects. They are distinct, and doing something to d
does not affect c
.
If in Java you learned about the distinction between 'primitive' types like int
and Object types like String
then one way to think about it is that in C++ all objects are primitive. If we look back at your code and use this 'C++ objects are like Java primitives' rule you can maybe see better what's wrong:
Java:
int[] players = new int[1];
players[0] = new int(playerWidth); // huh???
That should make it clear that the right hand side of the assignment should simply be a Player value rather than a dynamic allocation of a new player object. For an int in java this looks like players[0] = 100;
. Since Object types in Java are different Java doesn't have a way to write Object values the way you can write int
values. But C++ does; players[0] = Player(playerWidth, playerHeight, 20, 1);
The second problem is that arrays in C are weird and C++ inherited that.
Pointers in C and C++ allow 'pointer arithmetic. If you have a pointer to an object you can add to or subtract from it and get a pointer to a different object. Java has nothing similar to this.
int x[2]; // create an array of two ints, the ints are 'adjacent' to one another
// if you take the address for the first one and 'increment' it
// then you'll have a pointer to the second one.
int *i = &x[0]; // i is a pointer to the first element
int *j = &x[1]; // j is a pointer to the second element
// i + 1 equals j
// i equals j - 1
Additionally the array index operator []
works on pointers. x[5]
is equivalent to *(x+5)
. This means that pointers can be used as arrays, and that is idiomatic and expected in C and C++. In fact it's even baked into C++.
In C++ when you use new
to dynamically allocate an object, e.g. new Player
, you normally get a pointer to the type you specified. In this example you get Player *
. But when you dynamically allocate an array, e.g. new Player[5]
, it's different. Instead of getting back a pointer to an array of five Players
, you actually get back a pointer to the first element. This is just like any other Player *
:
Player *p = new Player; // not an array
Player *arr = new Player[5]; // an array
The only thing that makes this pointer different is that when you do pointer arithmetic on it you get pointers to valid Player
objects:
Player *x = p + 1; // not pointing at a valid Player
Player *y = arr + 3; // pointing at the fourth array element
new
and delete
are hard to use correctly if you use them without protection. To demonstrate this:
int *x = new int;
foo();
delete x;
This code is error prone and probably wrong. Specifically, if foo()
throws an exception then x
is leaked.
In C++ whenever you acquire a responsibility, such as when you call new
you acquire the responsibility to call delete
at a later time, you should remember
R.A.I.I.
Responsibility* Acquisition Is Initialization
* More frequently people say 'resource acquisition is initialization', but resources are only one kind of responsibility. I was persuaded to use the latter term by Jon Kalb in one of his Exception Safe C++ talks.
R.A.I.I. means that whenever you acquire a responsibility, it should look like you're initializing an object; specifically you're initializing a special object who's purpose is to manage that responsibility for you. One example of such an type is std::unique_ptr<int>
which will manage pointers to int
s allocated with new
:
C++:
std::unique_ptr<int> x(new int);
foo();
// no 'delete x;'
To manage your Player
array you'd use std::unqiue_ptr
like this:
std::unique_ptr<Player[]> players(new Player[1]);
players[0] = Player(playerWidth, playerHeight, 20, 1);
Now the unique_ptr
will handle that allocation for you and you don't need to call delete
yourself. (N.B. when you allocate an array you should give unique_ptr
an array type; std::unique_ptr<Player[]>
, and when you allocate anything else you use a non-array type, std::unique_ptr<Player>
.)
Of course C++ has an even more specialized R.A.I.I. type for managing arrays, std::vector
, and you should prefer that to using std::unique_ptr
:
std::vector<Player> players(1);
players[0] = Player(playerWidth, playerHeight, 20, 1);
Or in C++11:
std::vector<Player> players { Player(playerWidth, playerHeight, 20, 1) };
Your types don't match. And it's no wonder, you're trying to store a Player*
into an already-allocated Player
!
Player* players = new Player[1];
This creates an array of length 1, containing an instantiated Player
, and stores the whole thing into a Player*
. The type of players[0]
is going to be Player
.
players[0] = new Player(...)
This attempts to create a new Player*
and store it in the array. But the array contains Player
objects. You should just say
players[0] = Player(...)
Alternatively, and I'm going to guess this is more appropriate for you, you should stop using new
entirely, and use a std::vector
.
std::vector<Player> players;
players.push_back(Player(playerWidth, playerHeight, 20, 1));
// or players.emplace_back(playerWidth, playerHeight, 20, 1);
Not only is this much easier to use, but you also don't have to remember to delete
it later. When the std::vector
goes out of scope, it will automatically destruct. Also, unlike your array, std::vector
can contain any number of objects, so you can add new players or delete existing players at will.
There are other data structures as well that may possibly be more suited for you, depending on your exact use, but std::vector
is a good starting point.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With