In the previous chapter, we talked about class definitions and how to devise your own custom class. We discussed how by devising our own custom classes, we can construct variables that represented entities within your game or program.
In this chapter, we will talk about dynamic memory allocations and how to create space in memory for groups of objects.
Assume that we have a simplified version of class Player
, as before, with only a constructor and a destructor:
class Player { string name; int hp; public: Player(){ cout << "Player born" << endl; } ~Player(){ cout << "Player died" << endl; } };
We talked earlier about the scope of a variable in C++; to recap, the scope of a variable is the section of the program where that variable can be used. The scope of a variable is generally inside the block in which it was declared. A block is just any section of code contained between { and }. Here is a sample program that illustrates variable scope:
We mentioned previously that in general variables are destroyed when they go out of scope. Let's test this idea out with instances of class Player
:
int main() { Player player; // "Player born" } // "Player died" - player object destroyed here
The output of this program is as follows:
Player born Player died
The destructor for the player object is called at the end of the player object's scope. Since the scope of a variable is the block within which it is defined in the three lines of code, the Player
object would be destroyed immediately at the end of main()
, when it goes out of scope.
Now, let's try allocating a Player
object dynamically. What does that mean?
We use the new
keyword to allocate it!
int main() { // "dynamic allocation" – using keyword new! // this style of allocation means that the player object will // NOT be deleted automatically at the end of the block where // it was declared! Player *player = new Player(); } // NO automatic deletion!
The output of this program is as follows:
Player born
The player does not die! How do we kill the player? We must explicitly call delete
on the player
pointer.
The
delete
operator invokes the destructor on the object being deleted, as shown in the following code:
int main() { // "dynamic allocation" – using keyword new! Player *player = new Player(); delete player; // deletion invokes dtor }
The output of the program is as follows:
Player born Player died
So, only "normal" (or "automatic" also called as non-pointer type) variable types get destroyed at the end of the block in which they were declared. Pointer types (variables declared with *
and new
) are not automatically destroyed even when they go out of scope.
What is the use of this? Dynamic allocations let you control when an object is created and destroyed. This will come in handy later.
So dynamically allocated objects created with new
are not automatically deleted, unless you explicitly call delete
on them. There is a risk here! It is called a
memory leak. Memory leaks happen when an object allocated with new
is not ever deleted. What can happen is that if a lot of objects in your program are allocated with new
and then you stop using them, your computer will run out of memory eventually due to memory leakage.
Here is a ridiculous sample program to illustrate the problem:
#include <iostream> #include <string> using namespace std; class Player { string name; int hp; public: Player(){ cout << "Player born" << endl; } ~Player(){ cout << "Player died" << endl; } }; int main() { while( true ) // keep going forever, { // alloc.. Player *player = new Player(); // without delete == Memory Leak! } }
This program, if left to run long enough, will eventually gobble the computer's memory, as shown in the following screenshot:
Note that no one ever intends to write a program with this type of problem in it! Memory leak problems happen accidentally. You must take care of your memory allocations and delete
objects that are no longer in use.
3.144.9.124