Pointers and Inheritance


Although it may not seem like it from the information I've presented so far, C++ pointers contain a surprising amount of smarts. They enable a neat trick when you use them together with inheritance. Here's how it works.

Suppose you create a class called this_object. Your program uses the this_ object class as a base class. Imagine that the program also contains two more classes. One is called that_object and the other is named another_object. Both that_object and another_object inherit from this_object. Figure 11.3 illustrates the relationship of these three classes.

Figure 11.3. The that_object and another_object classes both inherit from this_object.


The tricky thing about pointers is that pointers to base objects can point to derived objects. Imagine again that you're writing a program with the three classes in the diagram in Figure 11.3. If your program declares a pointer to the this_object class, it can use that pointer to point to objects of type that_object and another_ object. To see why this is such an important feature of pointers, let's examine the program in Listing 11.4

Listing 11.4. Calling functions with base class pointers

 1     #include <cstdlib> 2     #include <iostream> 3 4     using namespace std; 5 6     class this_object 7     { 8     public: 9         virtual std::string Type() 10        { 11            return ("this_object"); 12        } 13    }; 14 15    class that_object : public this_object 16    { 17    public: 18        std::string Type() 19        { 20            return ("that_object"); 21        } 22 23    }; 24 25    class another_object : public this_object 26    { 27    public: 28        std::string Type() 29        { 30            return ("another_object"); 31        } 32 33    }; 34 35    int main(int argc, char *argv[]) 36    { 37        this_object *thePointer; 38 39        thePointer = new that_object; 40 41        if (thePointer == NULL) 42        { 43       return (-1); 44        } 45 46        cout << thePointer->Type() << endl; 47 48        delete thePointer; 49 50        thePointer = new another_object; 51 52        if (thePointer == NULL) 53        { 54            return (-1); 55        } 56 57        cout << thePointer->Type() << endl; 58 59        delete thePointer; 60 61        system("PAUSE"); 62        return EXIT_SUCCESS; 63    } 

The program in Listing 11.4 implements the three classes I've been discussing. As you can see from lines 15 and 25, both that_object and another_object inherit from this_object. All three classes contain a function called Type(). The type functions all return a string that states the type name of their respective classes. The important thing to note about the Type() function in the base class is that its definition contains the C++ keyword virtual. Whenever a member function starts with the keyword virtual, it means that if a derived class contains a function with the same name, return type, and parameter list, then the function in the derived class is called even if the pointer points to the base class! In the example in Listing 11.4, the derived class's Type() function overrides the Type() function in the base class. The pointer is smart enough to know that it's pointing at a derived class object rather than a base class object

So what's the purpose of that?

To discover the answer, read carefully through the main() function beginning on line 35. The first thing main() does is declare a pointer to a this_object, which is the base class for the other two. It then allocates a that_object and stores the object's address in the pointer to the base class. This is okay because that_object inherits from this_object. You can use a base class pointer to point to a derived object.

Nothing too tricky so far, but let's keep going.

If the allocation is not successful, the function returns a -1 to indicate an error. If it was successful, the program keeps going.

Line 46 contains a statement that is definitely tricky. It calls the Type() function using the pointer. Notice that it uses the C++ arrow notation (->) to call the Type() function. If it were to use a period like the programs did in previous chapters, the compiler would output an error. With pointers, you must use the arrow notation to call member functions. The arrow is made by a minus followed by a greater-than sign.

That's actually not the tricky part. It's coming next.

The variable thePointer is a pointer to this_object. However, it's pointing to a that_object.

Question: When the statement on line 46 is executed, which Type() function gets called? Will it be the one in the this_object class or the one in the that_object class?

Answer: The program calls the Type() function in the that_object class.

If your program uses a base class pointer to point to an object of a derived type, the pointer can be used to call functions in the derived class. This works only if the function in the base class has the keyword virtual in front of it, as the Type() function does in the this_object class. Putting in the keyword virtual tells the program to call the functions in the derived class whenever the pointer points to a derived object.

To prove its point, the program deletes the object on line 48 and allocates an object of type another_object on line 50. It calls the Type() function again on line 57. This time, the Type() function in the another_object class gets called.

You might be thinking, "I'll grant you that it's tricky, but what good is it?"

Note

If a program returns a nonzero value, it usually means there was an error. Typically, the error values are negative, but they do not have to be.


Imagine that the base class is named screen_object. Imagine that everything your program displays on the screen is derived from screen_object. Now suppose that the screen_object class has a virtual function in it called Render(). In that case, your program can use a pointer to a screen_object to call the Render() function for anything you want to display on the screen. You can put all the objects you want to draw into one list and move through the list, pointing to each object in turn with a pointer of type screen_object. For each object in the list, your program can call the Render() function and everything will draw itself to the screen.

In other words, you can create objects of type bomb, gun, flower, skunk, dragon, and so forth. All of these classes must be derived from screen_object. If they are, they all must have a Render() function. They can all go into the same list. You can use a loop that moves a pointer to a screen_object through the list. On each pass through the loop, you can call the Render() function for that object and the correct Render() function will be called.

The ability to call functions in derived classes using pointers to base classes is crucial to games. In fact, LlamaWorks2D uses exactly this technique. LlamaWorks2D actually provides a base class called screen_object. Your game can put any object derived from LlamaWorks2D's screen_object class into a level. All classes derived from screen_object must have a Render() function. LlamaWorks2D uses a screen_object pointer to call the Render() functions of all objects in a level. I'll demonstrate this technique in Part 5, so you'll get practice with it and can learn to use it in your games. Once you get the hang of it, you'll find it very straightforward to do and you'll see that it's absolutely essential for your games.



Creating Games in C++(c) A Step-by-Step Guide
Creating Games in C++: A Step-by-Step Guide
ISBN: 0735714347
EAN: 2147483647
Year: N/A
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net