Transcript Chapter 9
Beginning C++ Through Game Programming, Second Edition by Michael Dawson
Chapter 9
Advanced Classes and Dynamic Memory: Game Lobby
Objectives
• Combine objects • Use friend functions • Overload operators • Dynamically allocate and free memory • Avoid memory leaks • Produce deep copies of objects
Aggregation
• Game objects often composed of other objects – Drag racer: body, four tires, an engine • Other times, see an object as a collection of related objects – Zoo as a collection of animals • Can mimic these kinds of relationships among objects in OOP using aggregation
Using Object Data Members
• A data member that's an object itself • Has a relationship • A Critter object can have an attribute m_Name that is a string object
Critter Class
class Critter { public: Critter(const string& name = ""); string GetName() const; private: string m_Name; }; Critter::Critter(const string& name): m_Name(name) {} inline string Critter::GetName() const { return m_Name; }
Container Data Members
• You can also use containers as data members for your objects • A Farm object has a single data member that's a vector for Critter objects
Farm Class
class Farm { public: Farm(int spaces = 1); void Add(const Critter& aCritter); void RollCall() const; private: vector
Farm Class (cont.)
Farm::Farm(int spaces) { m_Critters.reserve(spaces); } void Farm::Add(const Critter& aCritter) { m_Critters.push_back(aCritter); } void Farm::RollCall() const { for (vector
Using Critter and Farm
int main() { Critter crit("Poochie"); cout << "Critter's name:" << crit.GetName() cout << "\nCreating critter farm.\n"; Farm myFarm(3); cout << "\nAdding three critters to farm.\n"; myFarm.Add(Critter("Moe")); myFarm.Add(Critter("Larry")); myFarm.Add(Critter("Curly")); cout << "\nCalling Roll...\n"; myFarm.RollCall(); return 0; }
Friend Functions
• • • Friend functions have complete access to any member of a class Specify function is friend of a class with friend function prototype inside class definition before In a Critter class: friend void Peek(const Critter& aCritter); • • Peek() can access any member of a Outside of Critter function Peek() : Critter object class definition, define global void Peek(const Critter& aCritter) { cout << aCritter.m_Name << endl; } • Peek() Critter can access private member m_Name object of a
Overloading Operators
• • • • Give meaning to built-in operators used with new types that you define Overload the Can send << operator so it can work with Critter object << to cout Critter Outside a Critter overloads << class, define a global function that operator ostream& operator<<(ostream& os, const Critter& aCritter) { objects os << "Critter Object - "; os << "m_Name: " << aCritter.m_Name; return os; } • Function can directly access the private data member m_Name of a Critter object because function is a friend of the Critter class friend ostream& operator<<(ostream& os, const Critter& aCritter);
Dynamically Allocating Memory
• Local variables live on the stack • Local variables don't persist beyond function in which declared • Heap memory persists until programmer frees it • Dynamic heap memory offers efficiency – Use only memory needed, free when done – Access memory even after a function ends (without having to return a copy of the object)
The new Operator
• Allocates memory on the heap and returns its address • Use new followed by the type of value int* pHeap = new int; • new int allocates memory on the heap for one int and returns its address • int* pHeap , declares a local pointer, pHeap , which points to the newly allocated chunk of memory on the heap • Can initialize memory on the heap at the same time you allocate it int* pHeap = new int(10);
Memory on the Heap Persists
int* intOnHeap() { int* pTemp = new int(20); return pTemp; } • Can access heap memory from another function int* pHeap2 = intOnHeap();
The delete Operator
• Memory on the heap must be explicitly freed delete pHeap; • Memory on heap pointed to by pHeap returned to the heap for future use is • Good rule of thumb—every new have a corresponding delete should
delete Pitfalls
• Freeing memory on the heap with delete does not alter pointers to the heap • When calling delete on a pointer, potential problem of dangling pointer • Never dereference dangling pointers • Can assign 0 to them, as in delete pHeap; pHeap = 0; • Or immediately assign dangling pointer valid memory address
Memory Leak 1
• Allocate memory on heap but have no way to free • Large enough leak, a program runs out of memory and crashes void leak1() { int* drip1 = new int(30); } • No way to free the allocated memory on heap that stores 30
Memory Leak 1 (cont.)
• To avoid – delete drip1 – in leak1() OR return copy of drip1 function and free memory later
Memory Leak 2
void leak2() { int* drip2 = new int(50); drip2 = new int(100); delete drip2; } • Assigning drip2 stores the 100 address of memory on the heap that leaks memory on the heap that stores 50
Memory Leak 2 (cont.)
• To avoid leak, delete drip2 memory address to it before assigning new
Declaring Data Members on the Heap
• Given the constructor of a Critter class Critter::Critter(const string& name, int age) { cout << "Constructor called\n"; m_pName = new string(name); m_Age = age; } • Each Critter object has data member, m_pName , that's a pointer to memory on the heap
Destructors
• Member function that’s called just before an object is destroyed • Most often used for cleanup • Must have the name of the class preceded by a tilde ( ~ ) character • No parameters and no return value • If you don’t write a destructor of your own, complier will supply a default destructor
Destructor Example
• If necessary to clean up memory on the heap —default constructor won't do ~Critter() { cout << "Destructor called.\n"; delete m_pName; } • When class allocates memory on the heap, write a destructor that frees that memory
Copy Constructors
• Sometimes an object is copied automatically • Copying is done by a special member function called the
copy constructor
• Occurs when an object is – Passed by value to a function – Returned from a function – Initialized to another object through an initializer – Provided as a single argument to the object’s constructor
Default Copy Constructor
• Default copy constructor is supplied if you don’t write your own • Copies the value of each data member to data members of the same name in the new object —shallow copy • For simple classes, default copy constructor is usually fine • For classes with a data member that points to value on the heap, usually write your own copy constructor
Default Copy Constructor Pitfalls
• Default copy constructor creates shallow copies • Potential problem if object has data member on the heap
Default Copy Constructor Pitfalls (cont.)
• Deleting shallow copy leaves original object with dangling pointer
Copy Constructor
• Copy constructor must have the same name as the class • Returns no value, but accepts a reference to an object of the class — the object that needs to be copied • Reference is almost always made a constant reference to protect the original object from being changed during the copy process
Copy Constructor Example
• Following allocates separate memory on the heap for the name of the new Critter object //copy constructor definition Critter::Critter(const Critter& c) { cout << "Copy Constructor called\n"; m_pName = new string(*(c.m_pName)); m_Age = c.m_Age; }
Copy Constructor Example Continued
• Copy constructor creates deep copy • When copy of object is destroyed, original is unaffected
Overloading the Assignment Operator
• When both sides of an assignment statement are objects of the same class, the class’ assignment operator member function is called • A default assignment operator member function is supplied for you if you don’t write one of your own • Default assignment operator provides only member wise duplication • For simple classes, the default assignment operator is usually fine • When you have a class with a data member that points to a value on the heap, you usually provide an overloaded assignment operator of your own; if you don’t, you’ll end up with shallow copies of objects when assigning one object to another.
Overloading the Assignment Operator
Critter& Critter::operator=(const Critter& c) //overloaded assignment op def { cout << "Overloaded Assignment Operator\n"; if (this != &c) { delete m_pName; m_pName = new string(*(c.m_pName)); m_Age = c.m_Age; } return *this; }
Summary
• Aggregation is the combining of objects so that one is part of another • Friend functions have complete access to any member of a class • Operator overloading allows you to define new meanings for built-in operators as they relate to objects of your own classes • The
stack
is an area of memory that is automatically managed for you and is used for local variables • The
heap
is an area of memory that the programmer can use to allocate and free memory
Summary (cont.)
• The new operator allocates memory on the heap and returns its address • The delete operator frees memory on the heap that was previously allocated • A dangling pointer points to an invalid memory location • Dereferencing or deleting a dangling pointer can cause your program to crash • A memory leak is an error in which memory that has been allocated becomes inaccessible and can no longer be freed
Summary (cont.)
• A
destructor
is a member function that’s called just before an object is destroyed • If you don’t write a destructor of your own, the complier will supply a default destructor for you • The
copy constructor
is a member function that’s invoked when an automatic copy of an object is made • A default copy constructor is supplied for a class if you don’t write one of your own • The default copy constructor simply copies the value of each data member to data members with the same names in the copy, producing a shallow copy
Summary (cont.)
• A
deep copy
is a copy of an object that has no chunks of memory in common with the original • A default assignment operator member function, which produces a shallow copy, is supplied for you if you don’t write one of your own