I need help understanding inheritance and virtual functions

  • Thread starter dwightarmyofchampions
  • Start date
D

dwightarmyofchampions

I'm a bit of a novice to C++ and I'm having a bit of trouble
understanding inheritance and virtual functions. I mean I understand
how one class can inherit a base class so you can have objects of the
derived class inherit the base class's member functions without having
to duplicate a whole bunch of code. But that's just timesaving, and I
know there's a whole lot more to it than that. And I definitely don't
understand how virtual functions can be the "single most important
topic from an OO perspective... C++ without 'virtual' is not OO," as
the C++ FAQ puts it. Could someone please point me to a lengthy book
and/or website that goes really in-depth into it, and preferably with
lots and lots of examples?
 
C

Christopher

I'm a bit of a novice to C++ and I'm having a bit of trouble
understanding inheritance and virtual functions. I mean I understand
how one class can inherit a base class so you can have objects of the
derived class inherit the base class's member functions without having
to duplicate a whole bunch of code. But that's just timesaving, and I
know there's a whole lot more to it than that. And I definitely don't
understand how virtual functions can be the "single most important
topic from an OO perspective... C++ without 'virtual' is not OO," as
the C++ FAQ puts it. Could someone please point me to a lengthy book
and/or website that goes really in-depth into it, and preferably with
lots and lots of examples?

Best example is to try it out
The way I like to think about it is (and my explanation might be
wrong) :
"a derived method overides the behavior of the virtual base class
method, _when the pointer of any type points to a derived type
object_"
"a derived method overides the behavior of a non virtual base class
method, _only when called from a derived type pointer_"

Try using each of these to call a virtual method and compare the
behavior when you delete the virtual keyword:

DerivedType * ptr1 = new DerivedType();
DerivedType * ptr2 = new BaseType();
BaseType * ptr3 = new DerivedType();
BaseType * ptr4 = new BaseType();
 
T

Tim Love

Could someone please point me to a lengthy book
and/or website that goes really in-depth into it, and preferably with
lots and lots of examples?

I can offer a standard example. The program has a list of shapes which need
redrawing. Each type of object has its own draw routine.
Compile the code below as is, run it, then add the word "virtual" as
indicated and compile+run again.


#include <iostream>
using namespace std;

class Point {
public:
float x;
float y;
};

class Shape {
public:
// Try adding "virtual" to the front of the next line.
void draw() { cout << "Shape's draw routine\n";} ;
// Note that replacing the above line by
// "virtual void draw()=0;" should force derived classes to
// have draw() routines. See if this is true.
float area;
};

class Triangle: public Shape {
Point points[3];
void draw() { cout << "Triangle's draw routine\n";} ;
};

class Rectangle: public Shape {
Point points[4];
void draw() { cout << "Rectangle's draw routine\n";} ;
};


int main() {

Triangle t1, t2;
Rectangle r1, r2;

Shape* shapes[4];
shapes[0]=&t1;
shapes[1]=&r1;
shapes[2]=&t2;
shapes[3]=&r2;

for (int i=0;i<4; i++)
shapes->draw();
}
 
D

dwightarmyofchampions

I compiled the program, both with and without the virtual keyowrd, but
I still don't quite get it.

How can the derived class override the base class's virtual draw()
function if you're defining an object of type Shape*? The derived
classes have things that the base classes do not (additional data
members and such), and now even though it's an object of type Shape*
(the base class), it can still behave like an object of the derived
class (i.e., accessing its derived draw() function)? And if that is
the case, why not just originally define the object to be of type
Derived* in the first place?

Shape* shapes[4];
shapes[0]=&t1;
shapes[1]=&r1;
shapes[2]=&t2;
shapes[3]=&r2;

^^^ Can you give a more detailed explanation as to what exactly is
going on here?
 
N

Neelesh

I compiled the program, both with and without the virtual keyowrd, but
I still don't quite get it.
Which program? It is always a good idea to keep the relevant context
of the original thread intact while you reply to it - it helps many
time.

[original thread]
class Shape{ public: virtual void draw(); };
class Rectangle: public Shape { public: virtual void draw(); };
class Triangle: public Shape { public: virtual void draw(); };

int main() {

Triangle t1, t2;
Rectangle r1, r2;

Shape* shapes[4];
shapes[0]=&t1;
shapes[1]=&r1;
shapes[2]=&t2;
shapes[3]=&r2;
}

[/end original thread]
How can the derived class override the base class's virtual draw()
function if you're defining an object of type Shape*?

You are _not_ defining an object of type Shape*. you are defining a
variable "shapes" that represents an Array of four Shape-pointers.
What is important here is to understand that the "shapes" array
doesn't itself contain Shape objects, rather it container four
pointers.
The derived
classes have things that the base classes do not (additional data
members and such), and now even though it's an object of type Shape*
(the base class), it can still behave like an object of the derived
class (i.e., accessing its derived draw() function)?

The object that is pointed by shapes[0], or shapes[1] etc is an object
of derived class. This is because we are clearly saying

shapes[0] = &t1; //t1 is a triangle object, and address of this
object is held by shapes[0]
Similarly
shapes[1] = &r1; //r1 is a rectangle object.
And if that is
the case, why not just originally define the object to be of type
Derived* in the first place?
Please understand again: Objects don't have types like "Derived*" or
"Base*" , they are the types of "pointers". Also, please note the
difference between a pointer, and an object. When we say

Shape* s = new Triangle();

We are creating an object of type Triangle (on heap), we are creating
a pointer of type Shape* (on stack) and we are putting the address of
the newly created Triangle object in the newly created Shape*
pointer.
 
J

Juha Nieminen

why not just originally define the object to be of type
Derived* in the first place?

I think the C++ streams are a perfect example of object-oriented
programming (including dynamic binding, ie. virtual functions) in
action. For example, suppose you have a function like this:

void printSomethingTo(std::eek:stream& os)
{
os << "Something";
}

std::eek:stream is a *base class* from which several other types of
stream classes have been derived. The printSomethingTo() function above
takes a reference of this base class type, but doesn't really know what
it's really given. However, it doesn't have to: It can still output the
string to whatever it was given, as long as it has been derived from
that base class.

The idea is that you can give it *different* types of objects as
parameter, and it will work with all of them. Example:

std::eek:fstream outputFile("somefile.txt");
printSomethingto(outputFile); // prints to a file

std::eek:stringstream aStringStream;
printSomethingTo(aStringStream); // prints to memory

printSomethingTo(std::cout); // prints to standard output

Note that the printSomethingTo() function is compiled only once, and
exists only once in the program. It's *not* compiled again for each
possible type it's given as parameter. (printSomethingTo() might even be
eg. in a precompiled library, and in the program itself you don't even
see how it has been implemented.) Yet still the above works: The
function is able to output the string to different targets without
problems. That's dynamic binding (ie. virtual functions) in action.
 
T

Tim Love

I compiled the program, both with and without the virtual keyowrd, but
I still don't quite get it.
How can the derived class override the base class's virtual draw()
function if you're defining an object of type Shape*?
As others have said, your terminology's not quite right but yes, this
is the crux of the issue.

Without "virtual", a thing pointed to by a Shape* pointer is
treated as a Shape. With "virtual", the thing pointed to by a
Shape* pointer is dealt with according to what it "really" is.

The shapes array in the example is the kind of thing that a
drawing package might use when items are grouped.
With "virtual" you get the best of both worlds - the top-level code
doesn't have to worry about unnecessary detail (if a new type of shape
is added to the code, the top-level code won't need to be changed) and each
type of shape retains its individuality - each can have its own draw()
member function (objects are best-place to do that job - they "know" about
themselves).
 
J

James Kanze

I think the C++ streams are a perfect example of
object-oriented programming (including dynamic binding, ie.
virtual functions) in action.

Actually, they are a masterful example of using the most
appropriate technique to support customization, depending on the
type of customization desired. They use polymorphism for one
type of customization, and function overloading for the other.
For example, suppose you have a function like this:
void printSomethingTo(std::eek:stream& os)
{
os << "Something";
}
std::eek:stream is a *base class* from which several other types
of stream classes have been derived. The printSomethingTo()
function above takes a reference of this base class type, but
doesn't really know what it's really given. However, it
doesn't have to: It can still output the string to whatever it
was given, as long as it has been derived from that base
class.

Yes, but that's not really a good example, because the functions
involved aren't virtual, and may not even be members. The
important point is that when the ostream, here, decides to
output a character, it calls a virtual function in the
associated streambuf. Which then does whatever is appropriate.
(It's actually a bit more complicated than that, because the
base class, streambuf, is more than just an interface; it uses
the template method pattern to customize its action, only
calling the virtual function when it has no room in its buffer.)
 
D

dwightarmyofchampions

Thanks for all the responses, everybody. And this is all good, but
like I said, is there a big massive book or chapter that explains this
concept in much greater detail?
 
B

Bart van Ingen Schenau

Thanks for all the responses, everybody. And this is all good, but
like I said, is there a big massive book or chapter that explains this
concept in much greater detail?

I can't recommend a specific book, but it seems you need a good
beginners book on OOA/OOD/OOP (Object Oriented Analysis / Design /
Programming). This topic is not specific to any particular language, so
look also for books that appear to be language agnostic or (if you don't
mind learning a different language before coming back to C++) use
another language for their code examples.

The people over in might also be able to give you some
book recommendations.

Bart v Ingen Schenau
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top