LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 06-18-2007, 05:48 PM   #1
erat123
Member
 
Registered: Oct 2006
Distribution: Ubuntu
Posts: 69

Rep: Reputation: 16
Inheritance vs. polymorphism


Hi All,

I'm trying to learn more about inheritance and polymorphism, and also, combining the two. I wrote this program in hopes that I could understand it better. I made two classes (a milk truck and a UPS truck class) that both inherit from a truck class. It's working pretty well, but I have one last function that determins if the truck can go at the required speed. That function is a virtual function, but I don't know how to make it work.

Code:
#include <iostream>
using namespace std;

class truck
{
protected:
	int pri_mph;
private:
	int pri_mpg;
public:
	int mph() { return pri_mph; }
	void mph(int mi_mph) { pri_mph = mi_mph; }
	int mpg() { return pri_mpg; }
	void mpg(int mi_mpg) { pri_mpg = mi_mpg; }
	virtual bool can_i_go_that_fast() = 0;
};

class cls_milk_truck : public truck
{
private:
	int pri_milkGal;
public:
	int milkGal() { return pri_milkGal; }
	void milkGal(int mi_gal) { pri_milkGal = mi_gal; }
	bool can_i_go_that_fast();
};

class cls_ups_truck : public truck
{
private:
	int pri_boxes;
public:
	int boxes() { return pri_boxes; }
	void boxes(int mi_boxes) { pri_boxes = mi_boxes; }
	bool can_i_go_that_fast();
};

bool cls_milk_truck::can_i_go_that_fast()
{
	return pri_mph > 30;
}

bool cls_ups_truck::can_i_go_that_fast()
{
	return pri_mph > 5;
}

int main()
{
	cls_milk_truck milk_truck;
	cls_ups_truck ups;

	// Milk Truck ********************************
	milk_truck.mpg(25);
	milk_truck.mph(15);
	milk_truck.milkGal(500);

	cout << "Milk Truck Stats:\n"
		<< "\tMPG: " << milk_truck.mpg() << endl
		<< "\tMPH: " << milk_truck.mph() << endl
		<< "\tGal: " << milk_truck.milkGal() << endl;
	cout << "Can it go that fast?  ";
	if (milk_truck.can_i_go_that_fast())
		cout << "Yes\n";
	else
		cout << "No\n";
	cout << "_____________________________________\n";

	// UPS Truck  ********************************
	ups.mph(40);
	ups.mpg(14);
	ups.boxes(50);

	cout << "UPS Truck Stats:\n"
		<< "\tMPG: " << ups.mpg() << endl
		<< "\tMPH: " << ups.mph() << endl
		<< "\tBoxes: " << ups.boxes() << endl;
	cout << "Can it go that fast?  ";
	if (milk_truck.can_i_go_that_fast())
		cout << "Yes\n";
	else
		cout << "No\n";


	return 0;
}
I know to make polymorphism work, I need to say something like:

Code:
truck *anotherUPS = new cls_ups_truck;
truck *anotherMilkTruck = new cls_milk_truck;
but, if i do that, then i can't access the can_i_go_that_fast() function.

thanks for any help!
 
Old 06-18-2007, 06:11 PM   #2
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
Quote:
I know to make polymorphism work, I need to say something like:

Code:

truck *anotherUPS = new cls_ups_truck; truck *anotherMilkTruck = new cls_milk_truck;


but, if i do that, then i can't access the can_i_go_that_fast() function.
You can because the function is a pure virtual function in the base abstract class, it will just look it up in the vtable and call the correct function.

Code:
truck *atruck = new cls_ups_truck;
if ( atruck->can_i_go_that_fast() )
{
   std::cout <<"faster faster, feel the wind in your hair\n";
}
 
Old 06-18-2007, 07:25 PM   #3
wjevans_7d1@yahoo.co
Member
 
Registered: Jun 2006
Location: Mariposa
Distribution: Slackware 9.1
Posts: 938

Rep: Reputation: 31
You're so close to making it work!

First, you have a dup-itis problem that has nothing to do with polymorphism; it simply makes your program work incorrectly as it is. Near the end of your code, when you're processing the UPS info, you do this:

Code:
	if (milk_truck.can_i_go_that_fast())
where you want to do this:

Code:
	if (ups.can_i_go_that_fast())
If you run the program as you posted it, both times you'll get "No" for "Can it go that fast?" If you correct the problem, you'll get "No" and "Yes", which I believe is what you want.

Now. On to polymorphism.

I modified your original program to use polymorphism by making the following changes.
  1. I changed the declarations of ups and milk_truck to truck *, and I initialized them the same way you suggested implementing anotherMilkTruck and anotherUPS.
  2. Of course, I changed the syntax so that each reference to an element of ups or milk_truck was done with "->", not ".".
  3. I had to add pure virtual function declarations for milkGal(), milkGal(int), boxes(), and boxes(int) to the base class truck, so that these functions could be called for objects whose classes were derived from class truck.
  4. I added error-emitting definitions for functions which are not to be used in each derived class, to keep one from trying to access, say, milkGal() for a UPS truck.

Here's the code. It works.

Code:
#include <iostream>
using namespace std;

void oops(char *message)
{
  cerr << message << endl;

  exit(1);
}

class truck
{
protected:
        int pri_mph;
private:
        int pri_mpg;
public:
        int mph() { return pri_mph; }
        void mph(int mi_mph) { pri_mph = mi_mph; }
        int mpg() { return pri_mpg; }
        void mpg(int mi_mpg) { pri_mpg = mi_mpg; }
        virtual bool can_i_go_that_fast() = 0;
        virtual int  milkGal()    = 0;
        virtual void milkGal(int) = 0;
        virtual int  boxes()      = 0;
        virtual void boxes(int)   = 0;
};

class cls_milk_truck : public truck
{
private:
        int pri_milkGal;
public:
        int milkGal() { return pri_milkGal; }
        void milkGal(int mi_gal) { pri_milkGal = mi_gal; }
        bool can_i_go_that_fast();
        void boxes(int) { oops("boxes(int) error for milk trucks"); }
        int  boxes()    { oops("boxes() error for milk trucks"); }
};

class cls_ups_truck : public truck
{
private:
        int pri_boxes;
public:
        int boxes() { return pri_boxes; }
        void boxes(int mi_boxes) { pri_boxes = mi_boxes; }
        bool can_i_go_that_fast();
        void milkGal(int) { oops("milkGal(int) error for UPS trucks"); }
        int  milkGal()    { oops("milkGal() error for UPS trucks"); }
};

bool cls_milk_truck::can_i_go_that_fast()
{
        return pri_mph > 30;
}

bool cls_ups_truck::can_i_go_that_fast()
{
        return pri_mph > 5;
}

int main()
{
        truck *milk_truck=new cls_milk_truck;
        truck *ups       =new cls_ups_truck;
/*
        cls_milk_truck milk_truck;
        cls_ups_truck ups;
*/

        // Milk Truck ********************************
        milk_truck->mpg(25);
        milk_truck->mph(15);
        milk_truck->milkGal(500);

        cout << "Milk Truck Stats:\n"
                << "\tMPG: " << milk_truck->mpg() << endl
                << "\tMPH: " << milk_truck->mph() << endl
                << "\tGal: " << milk_truck->milkGal() << endl;
        cout << "Can it go that fast?  ";
        if (milk_truck->can_i_go_that_fast())
                cout << "Yes\n";
        else
                cout << "No\n";
        cout << "_____________________________________\n";

        // UPS Truck  ********************************
        ups->mph(40);
        ups->mpg(14);
        ups->boxes(50);

        cout << "UPS Truck Stats:\n"
                << "\tMPG: " << ups->mpg() << endl
                << "\tMPH: " << ups->mph() << endl
                << "\tBoxes: " << ups->boxes() << endl;
        cout << "Can it go that fast?  ";
        if (ups->can_i_go_that_fast())
                cout << "Yes\n";
        else
                cout << "No\n";


        return 0;
}
Hope this helps.
 
Old 06-18-2007, 08:08 PM   #4
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
I do not really think that creating a "blob" is the best way of achieving what the OP is after and to be blunt is very bad design. With a little re-factoring it could be made much better and not exit the application. The following:
Code:
        virtual int  milkGal()    = 0;
        virtual void milkGal(int) = 0;
        virtual int  boxes()      = 0;
        virtual void boxes(int)   = 0;
could be changed, what do they have in common? they are the loads which the trucks can carry so why not use that information and instead have
Code:
        virtual int const&  load()const = 0;
        virtual void load(int const& ) = 0;
In fact when you look at it like that they dont even need to be virtual functions as they could be stored in the base class
Code:
class Truk
{
protected://just so derived classes can access
int m_max_load;//derived classes could set this in there constructor initailisation list
int m_current_load;
public:
....
        int const&  load()const{return m_current_load;}
        void load(int const& l){m_current_load = l;}
....

Last edited by dmail; 06-18-2007 at 08:11 PM.
 
Old 06-18-2007, 09:35 PM   #5
sundialsvcs
LQ Guru
 
Registered: Feb 2004
Location: SE Tennessee, USA
Distribution: Gentoo, LFS
Posts: 10,671
Blog Entries: 4

Rep: Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945Reputation: 3945
It might help to consider that inheritance and polymorphism are really two sides .. but opposite sides .. of the same coin.

Inheritance tries to exploit the cases where "X is just the same as Y except..."

Meanwhile, polymorphism tries to exploit, "Y is just the same as X, except..."

Uh huh, "X and Y are reversed .. that's the only difference between the two .." but what a difference it is!

In each case, what you're shooting for is the same goal: economy of code. There is, in each case, a "common set of code" that lies at the intersection between "X" and "Y," and in either case you are trying to exploit that commonality. But the two cases will never be equal because in one case you are exploiting the union between the two sets and in the other case you are exploiting the difference.

P.S. Such an incredibly deep distinction, occurring as it is at such a late hour of the evening (in this part of the planet, anyhow) is definitely best-appreciated under the influence of good beer. In fact, under such influences it is positively brilliant...
 
Old 06-19-2007, 12:57 AM   #6
erat123
Member
 
Registered: Oct 2006
Distribution: Ubuntu
Posts: 69

Original Poster
Rep: Reputation: 16
thanks everyone for all the replies! wjevans_7d1@yahoo.co, thanks for catching that error with the use of the wrong object (milk instead of ups). dmail, you're right, i dont want to use "bulk" code as you called it. and i realize that i can categorize the contents of both trucks as "load" but what happens when an example comes around that doesn't allow something that simple?

i guess i'm also still very confused about the syntax of polymorphism. i thought i understood it, but now i'm thinking otherwise. can someone give me a definition of virtual and = 0 at the end of the functions. i guess i just used them b/c they didn't provide an error in the end.

thanks again for everyone's help!
 
Old 06-19-2007, 03:18 AM   #7
graemef
Senior Member
 
Registered: Nov 2005
Location: Hanoi
Distribution: Fedora 13, Ubuntu 10.04
Posts: 2,379

Rep: Reputation: 148Reputation: 148
If a class method is defined as virtual then the compiler will build a virtual lookup table. What this means is that when the program runs the code will need to visit the lookup table to decide which of the methods to actually call. Is is the truck version of the method, the cls_milk_truck version or the cls_ups_truck version. It decides which one by looking at the actual class of the object. Let's just assume that the actual object is a cls_farmyard_milk_truck then it will look up the virtual table for the version to use, since this class doesn't have a can_i_go_that_fast() it will then look at the parent class, in this case the cls_milk_truck, which does and so that version will be called.

Next comes the =0, this is associated with virtual functions and in C++ they are called pure virtual functions. A pure virtual function differ from virtual functions in that they don't have a definition (source code). Because they have not been defined they can't be called, and what is more it means that an object of that class can't be instantiated, the class is referred to as an abstract class and child classes would need to provide the code for this method if object of the child classes are to be created. However, a pointer to an abstract class can be declared, it must be instantiated to a concrete class, such as:

truck * myVehicle;
myVehicle = new cls_milk_truck();

Finally virtual functions can be called statically, that is it is possible to tell the compiler that under these circumstances call this version of the virtual function. For example the can_i_go_that_fast() of the cls_farmyard_milk_truck could be written to ensure that farmyard milk trucks are half the speed of their road running cousins, as follows:
Code:
bool cls_farmyard_milk_truck::can_i_go_that_fast()
{
   int tmp_mph = pri_mph;
   pri_mph /= 2;
   bool result = cls_milk_truck::can_i_go_that_fast()
   pri_mph = tmp_mph;
   return result;
}
 
Old 06-19-2007, 11:41 PM   #8
erat123
Member
 
Registered: Oct 2006
Distribution: Ubuntu
Posts: 69

Original Poster
Rep: Reputation: 16
Thanks graemef! That helped out a lot!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
overloading vs polymorphism lbdgwgt Programming 1 03-06-2007 02:30 PM
C++ Polymorphism: small question cdog Programming 13 01-13-2007 08:30 PM
Polymorphism problem lucky6969b Programming 3 12-29-2005 02:59 AM
Polymorphism in VBA ashirazi Programming 4 07-05-2005 04:44 PM
polymorphism in java jwn7 Programming 4 11-17-2004 04:20 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:15 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration