Virtual Class Functions

Started by
6 comments, last by GameDev.net 24 years, 4 months ago
Virtual functions are how C++ implements polymorphism. This is, IMHO, the greatest feature of C++.

Here's a simple example:
class A
{
. . .
public:
virtual void Foo();
};

class B : public A
{
. . .
public:
virtual void Foo();
};

Then elsewhere, I can create a 'B' by saying:
A* pA = new B; // A pointer to 'A' can point to an A or any of its descendents.

Then, pa->Foo() will call B's Foo, instead of A's. If it weren't declared virtual, A's would be called.

This feature is useful for allowing descendent classes to override their parents behavior, for implementing an interface as a base class, then creating several implementations of that interface as descendents, etc.

Advertisement
It's useful if you're going to make polymorphic classes.

If you've done any Windows programming I'm sure that you've read that everything on the screen is a window (duh, right) Even the scroll bars, the menu bars, text boxes buttons etc. are considered windows as far as the OS is concerned. It just sends generic messages to these 'windows' and doesn't care what they do with them. As far as it's concerned they're identical, even though we can tell they're vastly different.

Although Windows isn't written in C++ the concept is the same. In game programming a good example would be some arcade game where the entities perform vastly different and custom actions (i.e. one bad guy dies and explodes into four midget versions of himself that attack you, another makes one last death throw attack at you, another just dies) where it doesn't make sense to just create flags for each unique action. You can make a virtual bad guy class:

class generic_bad_guy{public:    generic_bad_guy();    virtual ~generic_bad_guy();   // this should always be virtual for a base class    virtual void actionDead(void) = 0;  // pure virtual function, doesn't even exist}

And a few classes based on this:

class bad_guy_1 : public generic_bad_guy{public:    virtual void actionDead(void){explode();}}class bad_guy_2 : public generic_bad_guy{public:    virtual void actionDead(void){attack_one_more_time();}}

Of course you're probably saying "i could do that anyway, with inheritance", but you couldn't do this:

generic_bad_guy*  badGuys[3];badGuy[0] = new bad_guy_1;badGuy[1] = new bad_guy_2;badGuy[2] = new bad_guy_3;//here's where it gets really crazyfor(int i = 0; i < 3; i++){    badGuy->actionDead();<BR>}<BR></pre><P>In an array or linked list, it doesn't care what type of bad guy it really is, it just calls the appropriate function at run-time and does completely different things.<P>Polymorphism isn't useful for everything, but when it is you can really start to kick out the wu-tang style in your code. <BR>

-the logistical one-http://members.bellatlantic.net/~olsongt

Zack,

Well, in your example I can agree it wouldn't be much use. However, let me try to present another example and see if it helps make a little sense out of it.

//: abstract shape class
//:
class shape {

virtual void draw() = 0;
};

//: triangle class that inherits from
//: shape and implements draw()
class triangle : public shape {

void draw() { /*draw triangle */ }
};

//: square class that inherits from
//: shape and implements draw()
class square : public shape {
void draw() { /* draw square */
};


Polymorphism is provided through Inheritence, a key feature of the C++ language. Virtual functions are the backbone of polymorphism, another of the key features of C++.

With the above set of classes, polymorphism allows us to call the draw member of any class derived from shape, and have it resolve to the correct draw member.

For example, if you have a list of shape pointers, you could iterate through the list calling draw() and the correct draw member would be called for each shape type: triangle, square, or whatever.

check out:
http://members.xoom.com/zbonham/type.cpp

This is more of an example of RTTI (Run Time Type Info), however it does demonstrate what I am trying to explain here.

Any good C++ reference should give a more adequate explaination than what I could give here.

HTH,

-mordell

__________________________________________

Yeah, sure... we are laughing WITH you ...

Wow! I swear there wasn't a response to the orginal post a minute ago!!

__________________________________________

Yeah, sure... we are laughing WITH you ...
In your example, don't use virtual...virtual is INEFFICIENT when it is not being used for a purpose, because it causes your program to need one extra level of indirection (one more pointer to dereference).

Virutal IS usefull however, for the reasons mentioned above, for the effect it has on RUN TIME behavior of derived classes. If you understand polymorphism, you will understand this. Otherwise, it is a good idea to read about this subject. ANY good object oriented programming book will talk about polymorphism, just go to the bookstore, open the index and look for "polymorphism" or "inheiritance".

I have to agree with Xai. Just like drinking and everything else in the world, practice moderation and you will get the maximum benefit for the least drawback. Virtual functions do cause additional overhead in that the executable must keep tables of pointers so that base classes can call the correct function. This overhead isn't much but it is significant.

I used to have a Java book that talked about the expense of operations such as polymorphism, casting, etc etc. The relationship is staggering. For instance casting is some crazy number of times more processor expensive than instantiating the actual type. I believe calling a pA->foo(); where pA is a B causes pA to be cast into a pB and then passed as a *this to foo() of a B. Please correct me if I am wrong. In this case the cost of polymorphism is the cost of the cast and the additional dereference Xai mentioned.

Kressilac

ps This is the major drawback to polymorphism. If we could get around it IMHO polymorphism would be the greatest tool C++ delivered. Think about how much more expensive traversing a list of pA pointing to a B would be versus a list of pB pointing to B. The more elements you have the more casting up and down the class hierarchy occurs. Expensive.
------------------
Derek Licciardi

[This message has been edited by kressilac (edited December 13, 1999).]

Derek Licciardi (Kressilac)Elysian Productions Inc.

Why would I ever need to use virtual functions in my class? What
good are they?

What's the real difference between these two?


code:
class TEST_CLASS { protected: int num; public: TEST_CLASS(void); ~TEST_CLASS(void); int Get_Num() { return num; }};


code:
class TEST_CLASS { protected: int num; public: TEST_CLASS(void); ~TEST_CLASS(void); virtual int Get_Num() { return num; }};


Zack

No cast should be done during a virtual function call. It's just a table lookup, which isn't too bad if it is in the cache.

Also, if a function is virtual, but the type of the pointer is known at run time (it's not a pointer or reference) then no runtime overhead should incur, assuming the compiler is decent.

This topic is closed to new replies.

Advertisement