Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   C++ (http://www.programmingforums.org/forum15.html)
-   -   What is the point of pure virtual functions? (http://www.programmingforums.org/showthread.php?t=9369)

aznluvsmc Apr 13th, 2006 9:29 PM

What is the point of pure virtual functions?
 
I was reading up on pure virtual functions in C++ Primer Plus 5th edition and I'm still a little unclear as to what their purpose is. From my understanding, they are used in abstract base classes so that an object cannot directly be created from the class but rather other classes must derive from it. In this case, any function declared as pure virtual using "=0" MUST be defined within the derived classes.

Is this essentially what pure virtual functions are ALL about?

grumpy Apr 13th, 2006 11:44 PM

Very loosely, yes. You've mixed up a couple of terms.

If you wish to create a set of classes that are all derived from a common base, but don't wish to instantiate the base class, give it at least one pure virtual function. For example, if your base class is Fish, and derived classes represent specific species of fish, it does not make sense to instantiate a Fish.

The other purpose of pure virtual functions is to force derived classes to specialise particular behaviours offered by the base class. All a user of the class needs is a pointer (or reference) to the base class, and it can call a member function and know that the behaviour for the actual (instantiated) class will occur. Using my Fish example, one candidate for a pure virtual function would be named Feed(). All types of fish will have some type of feeding behaviour, but it doesn't necessarily make sense for a representation of a generic Fish to to be specific about what that behaviour is.

Technically, a base class can declare a pure virtual function, and also [optionally] define it (provide an implementation). A derived class, unless it overrides that inherited function (i.e. declares it) cannot be instantiated. As a rough rule, if a derived class overrides an inherited pure virtual function, it must also provide a definition (i.e. provide an implementation of it). The compiler won't complain about a lack of definition of an overridden virtual function, but the linker probably will (with the result that it is not possible to build the overall application).

Destructors can also be pure virtual: a simple way to ensure a base class cannot be instantiated even if it has no other pure virtual functions.

InfoGeek Apr 14th, 2006 2:30 AM

Quote:

Originally Posted by grumpy
Technically, a base class can declare a pure virtual function, and also [optionally] define it (provide an implementation).

In what situation should we define a pure virtual function?

grumpy Apr 14th, 2006 2:42 AM

A pure virtual destructor should always be defined, as the base class destructor will always be implicitly invoked when an object in the hierarchy is destroyed. If that pure virtual destructor is not defined, the typical result will be a linker error.

InfoGeek Apr 14th, 2006 2:56 AM

thanks grumpy.

Jessehk Apr 14th, 2006 9:02 AM

Grumpy: These useful, informative explanations are very helpful. Thanks :)

Quick question: If the declaration of a pure virtual function in an ABC simply means that the method will be defined in a derived class, what is the point? Why not simply declare
a class for each Fish and include a unique definition of feed() for each one?

InfoGeek Apr 14th, 2006 9:12 AM

Quote:

Why not simply declare a class for each Fish and include a unique definition of feed() for each one?
That's called polymorphism. One interface, multiple methods. You can have a pointer(pointing to base class fish) that may contain the address of an object of either fish and you don't need to bother yourself about which fish object it is. Simply call the feed() method and the fish will be apropriately feeded.

grumpy Apr 14th, 2006 9:47 AM

To expand on InfoGeek's response, Jessehk, let's say you want to maintain a list of two fish in a tank. You might do this via an array of pointers to fish (as each fish may be a different kind).

:

//  declaration of Fish and derived classes

void FishFeedFrenzy(Fish **list, int nfish)
{
    for (int i = 0; i < nfish; ++i)
      list[i]->Feed();
}

int main()
{
    Fish *fishies[2];
    fishies[0] = new GoldFish;
    fishies[1] = new Catfish;

    drop_food_in_tank();   
    FishFeedFrenzy(fishies, 2);

    //  kill the goldfish and replace it with a Guppie

    // the following code does this if Fish has a virtual destructor

    delete fishies[0];
    fishies[0] = new Guppie;   

    drop_food_in_tank();   
    FishFeedFrenzy(fishies, 2);
}

If Feed() is a virtual function of Fish, the FishFeedFrenzy() function doesn't need to know what each type of fish is; it just tells each fish to feed itself. If Feed() is a pure virtual function of Fish, then every derived class (Goldfish, Guppie, etc) is forced to implement appropriate feeding behaviour by overriding the Feed() member function.

If Goldfish, Guppy, etc are not derived from a common base class, the code would be much more complicated. For example;
:

//  declaration of Fish and derived classes

void FishFeedFrenzy(void **list, int nfish)
{
    // note our implementation is more complicated.
    //    This would be required if
    //      1)  Feed() is not a virtual member of Fish  OR
    //      2)  Specific types of fish are not derived from the Fish class

    for (int i = 0; i < nfish; ++i)
    {
        // Note:  the implementations of IsGoldfish() and similar functions below
        //    will be non-trivial, and I won't suggest a way of implementing them

        if (IsGoldfish(list[i]))
            ((Goldfish *)list[i])->Feed();
        else if (IsGuppy(list[i]))
            ((Guppy *)list[i])->Feed();
        else
            // similar entries to be maintained for all fish types
    }
}

int main()
{
    void *fishies[2];
    fishies[0] = new GoldFish;
    fishies[1] = new Catfish;

    drop_food_in_tank();   
    FishFeedFrenzy(fishies, 2);

    //  kill the goldfish and replace it with a Guppie
    //    the process of destroying a fish is also more involved.

    if (IsGoldFish(fishies[0]) delete ((Goldfish *)fishies[0]);
    fishies[0] = new Guppie;   

    drop_food_in_tank();   
    FishFeedFrenzy(fishies, 2);
}

The first example, which makes use of polymorphism, is (I would suggest) somewhat easier to implement. It would also be easier to introduce new types of fish (by deriving from Fish) into the program without rewriting most of it. It would also be easier to reconfigure the program so it doesn't rely on having a Goldfish as the first fish put in the tank.

Jessehk Apr 14th, 2006 10:07 AM

I get it now. Thank you for taking the time to respond, Grumpy and InfoGeek. :)

aznluvsmc Apr 14th, 2006 2:48 PM

Thanks for the response. So to take your Fish class example, if the Fish base class specified two methods called Feed() as a pure virtual function and Swim() as just a regular function with a definition (we'll assume all derived fishes swim the same way) then a derived class of GoldFish and Salmon can just use the Swim() function but MUST redefine the Feed() function for their particular type?


All times are GMT -5. The time now is 10:51 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC