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 crea
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.
Here you have allocated some memory to store an array of one Player (not really useful but it's a first step).
Your variable "players" is now storing the address of the first (and only) slot in this array. Then by accessing the first Player with players[0], you are able to directly write/read in its memory and no more allocation is needed.
I would like to propose this as a resolution to the problem of setting value of index 0:
Player* players = new Player[1];
players[0] = *(new Player(playerWidth, playerHeight, 20, 1));
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) };
In Java, you do Foo f = new Foo();
, giving you a dynamically allocated object who's lifetime is managed by the garbage collector.
Now, in C++, Foo* f = new Foo;
looks similar and also gives you a dynamically allocated object (which you can access via the pointer f
), but C++ doesn't have a built-in garbage collector. In most cases, the functional C++ equivalent is Foo f;
, which gives you a local object that is destroyed when you leave the current function (via return or throw).
If you need dynamic allocation, use "smart pointers", which are actually classes that behave like pointers. In C++ 98, there is only std::auto_ptr
and people often use boost::shared_ptr
to complement it. In the newer C++ 11, there are std::unique_ptr
and std::shared_ptr
that achieve the same.
I hope this gives you some pointers in directions where you need to read a bit, but overall Juanchopanza gave a good advise: Don't use new
unless you really need to. Good luck!
The reason is, type of your variable
players[0]
is Player (object). However, operator "new" (new Player) returns a pointer (Player*)
If you want to have only one object, correct way to do it will be:
Player* player = new Player(playerWidth, playerHeight, 20, 1);
And don't forget in C++ you need to clean the mess after yourself - somewhere in the end call
delete player;
for every object you've created. C++ does not have Garbage Collector - meaning all manually created (by "new") objects stay until you manually delete them.