[comp.lang.c++] virtual vs. non-virtual functions.

frodo@ncr-fc.FtCollins.NCR.COM (David Fletcher) (03/10/89)

Hello.  First of all, this is under AT&T 1.2.1 cfront.  I have a base
class, B, and a publicly derived class, D.  The base class defines a
non-virtual public member function, Value(), that returns the value of
some protected entry.  D does not define this member function (&, again,
Value() is not virtual).

Out of curiosity I tried:
	.
	.
   B SomeDooDad;
   D OtherDooDad;

   cout << "B's value is " << SomeDooDad.Value() "\n";
   cout << "D's value is " << OtherDooDad.Value() "\n";
	.
	.

This works but I didn't expect it to & don't understand why.  I thought
that only virtual functions (defined in a based class) would be defined
for the derived class.  If that's not the case then why even have the
virtual keyword?

Is this a bug?  Am I wrong?  Thanks for the help.
-- 
David Fletcher, NCR Microelectronics
2001 Danfield Court,
Ft. Collins, CO 80525	  |	"... Let everything else go ..."
(303) 223-5100 x 241	  |			-- Phil Keaggy

beard@ux3.lbl.gov (Patrick C Beard) (03/10/89)

I am under the impression that the virtual keyword implies that a
function is redefinable in a derived class.  A non-virtual function
isn't, and so is callable from derived classes.  It would have worked
as well if you had declared "Value" as virtual, and you would have been
able to define a new version of Value() for the derived class D.

Correct me if I'm wrong, I'm fairly new to C++ as well.


+----------------------------------------------------------------+
 \   Patrick Beard                 "Badges?                       \
  \    Berkeley Systems, Inc.        I ain't got to show you...    \
   \      PCBeard@lbl.gov                 ...NO STINKING BADGES!"   \
    + ---------------------------------------------------------------+

frodo@ncr-fc.FtCollins.NCR.COM (David Fletcher) (03/10/89)

// In response to my original question Patrick Beard writes:
    I am under the impression that the virtual keyword implies that a
    function is redefinable in a derived class.  A non-virtual function
    isn't, and so is callable from derived classes.  It would have worked
    as well if you had declared "Value" as virtual, and you would have been
    able to define a new version of Value() for the derived class D.

I agree with most of what you say but I *can* redefine the function in
the derived class.  In fact, on page 194 of "The C++ Programming
Language" an example is given showing that the non-virtual function can
be redefined in the derived (manager) class.

What I don't understand is why the Value() function, if declared in only
the base class in a non-virtual way, is visible to the derived class.

Adding the keyword virtual buys me 2 things (as I understand it):
1. I can assure that the return type of the redefined virtual function
   is compatible with the original version.  When base's Value() function
   is vitual and returns an int and derived's Value() function returns a
   long then CC gives me an error (as I would expect it to).  If Value() is
   not virtual & in the derived class I declare it to return a long then
   the compiler is quite happy.

2. A call to Value() via a pointer to an object (of type base OR derived)
   would call the correct version of Value().  This is explained fairly
   well in "The C++ Book."

I guess what my question boils down to is that I thought data would
be inherited and (member) functions wouldn't UNLESS a function was
declared virtual.  Thanks again for any help (Thanks, too, Patrick).

  
-- 
David Fletcher, NCR Microelectronics
2001 Danfield Court,
Ft. Collins, CO 80525	  |	"... Let everything else go ..."
(303) 223-5100 x 241	  |			-- Phil Keaggy

scp@raven.lanl.gov (Stephen Pope) (03/11/89)

David Fletcher writes <202@ncr-fc.FtCollins.NCR.COM>:

> Adding the keyword virtual buys me 2 things (as I understand it):
> 1. I can assure that the return type of the redefined virtual function
>    is compatible with the original version.  When base's Value() function
>    is vitual and returns an int and derived's Value() function returns a
>    long then CC gives me an error (as I would expect it to).  If Value() is
>    not virtual & in the derived class I declare it to return a long then
>    the compiler is quite happy.

> 2. A call to Value() via a pointer to an object (of type base OR derived)
>    would call the correct version of Value().  This is explained fairly
>    well in "The C++ Book."

> I guess what my question boils down to is that I thought data would
> be inherited and (member) functions wouldn't UNLESS a function was
> declared virtual.

The behavior you observe is correct.  As long as your derived class
is *publically* derived from the base class, all protected and public
members *and* member functions are visible to the derived class.

The 1st point above is not a *reason* for the virtual keyword, but rather
a consequence of it.  Point 2 is the _meat_and_potatoes_ of the issue:
If a derived class overloads a non-virtual base class definition,
then invocations of the said member function from a pointer to the
base class (regardless of whether the pointer is actually pointing
to a base class or derived class object) will always invoke the
base class method.  That is:

class B {
    public:
	void func() { cout << "Base\n"; };
};

class D : public B {
    public:
	void func() { cout << "Derived\n"; };
};

main()
{
    B b;
    D d;
    B *bp;

    b.func();		// prints "Base"
    d.func();		// prints "Derived"
    bp = &b;
    bp->func();		// prints "Base"
    bp = &d;
    bp->func();		// prints "Base"
}


If, however, func had been declared virtual in class B, the last statement
above would generate "Derived", not "Base".

The issue is that the virtual declaration insures that invocations of a
member function are called by looking up a function's address in the
virtual pointer table, thus the proper derived version will be called
even when referenced through a pointer to the base class.  If the
member function is not a virtual, it has no entry in the virtual
pointer table, and since all the compiler knows is that it has a
pointer to base class, the best it can do is invoke the member function
*for the base class*. It has no way of knowing that the object
pointed to might be anything other than the base class!

This also brings up an issue skirted a little while ago:  why can't
a virtually overloaded member function return different types
in for derived class than for the base class.  The fact is that if
all you have is a pointer to a base class, and you invoke a virtual
member function, you *must* know exactly what is being returned.
Since all you know is the base class properties, the derived class
version better return the same data type, or else you will munge
the return value horribly.  Only in a language with self-identifying
(tagged) data structures could you return differing types.

Stephen C. Pope
The Santa Fe Institute
scp@santafe.edu

kanner@Apple.COM (Herbert Kanner) (03/11/89)

In article <202@ncr-fc.FtCollins.NCR.COM> frodo@ncr-fc.UUCP (David Fletcher) writes:
>I guess what my question boils down to is that I thought data would
>be inherited and (member) functions wouldn't UNLESS a function was
>declared virtual.  Thanks again for any help (Thanks, too, Patrick).
>
>  
>-- 
>David Fletcher, NCR Microelectronics
>2001 Danfield Court,
>Ft. Collins, CO 80525	  |	"... Let everything else go ..."
>(303) 223-5100 x 241	  |			-- Phil Keaggy

Here are the facts.  Both virtual and non-virtual member functions are
inherited from a base class to a derived class, and either of them can
be overriden in a derived class.  The distinction between virtual and
non-virtual functions is the non-virtul function are *always* bound at
compile time, while virtual function will, under the right
circumstances be bound at run time.

The circumstance under which binding takes place at run time is the
following:  

class Base {
public:
  	virtual void f();
};

class Derived : public Base {
	void f();
};

Base* ptr;

ptr = new Base;
ptr->f(); // will call the f() in Base

ptr = new Derived;
ptr->f(); //will call the f() in Derived

Had the function not been declared virtual, both calls would have been
to the f() in Base, because the decision would have been made at
compile time and would have been determined by the type of ptr.


-- 
Herb Kanner
Apple Computer, Inc.
{idi,nsc}!apple!kanner
kanner@apple.com