virtual function being private in derived class

S

subramanian100in

The following program contains virtual function in the derived class
under private access label. This is for learning purpose only:
Consider the program x.cpp:

#include <cstdlib>
#include <iostream>

using namespace std;

class Base
{
public:
virtual void print(void) const;
};

inline void Base::print(void) const
{
cout << "from Base::print()" << endl;

return;
}

class Middle : public Base
{
private:
virtual void print(void) const;
};

inline void Middle::print(void) const
{
cout << "from Middle::print()" << endl;

return;
}

int main()
{
Middle m;
Base* p = &m;
p->print();

return EXIT_SUCCESS;
}

This program compiles fine with g++3.4.3 as
g++ -std=c++98 -pedantic -Wall -Wextra x.cpp

When run, it produces the following output:
from Middle::print()

This output corresponds to the derived class Middle::print() function.
But this function is private in 'Middle'. My question is how a private
function could be invoked. Is this the expected behavior ?

Kindly explain.

Thanks
V.Subramanian
 
R

Robert Fendt

Hi,

an additional note on the (on first glance maybe strange)
behaviour of C++ to separate virtuality and access level: by
overriding/implementing a virtual function and at the same time
declaring it private, you force that this particular
functionality of the class be only used via the base class
(which is for example only an interface definition). This can
actually be useful (think e.g. of abstract factories).

Apart from that, (and I know it does not correspond to your
original question, but you *did* say you are currently learning
C++), you should consider limiting your use of public virtuals
in C++ anyway. However, I will not say (like others do
sometimes) "do not use them at all".

They have their uses, and there's nothing wrong with them if you
use them e.g. to define an abstract interface without inheriting
further from the implementing classes. However, there are
problems that newcomers to C++ often do not expect, especially
if they come from a Java or C# background. The two main sources
of problems are (in my experience):

(1) There is (currently) no 'override' annotation. C++ 0x *will*
have it, but it will probably still take several years before
usable implementations actually become available. In current
standard C++, you can inadvertently create a new function
instead of overriding the base definition without realising it.

(2) Virtual functions and function overloading are a dangerous
mixture in C++. Consider two virtual functions which differ
only by their signatures: now overriding just one in a derived
class actually hides the other if you do not explicitly
instruct the compiler to do otherwise. This is especially
irksome in conjunction with (1).

To help at least with point (2), the "non-virtual interface
pattern" has gained some popularity. In the interface class, the
public (possibly overloaded) functions are declared and defined
as non-virtual functions which in turn call non-overloaded,
private, purely virtual functions. This separates overloading and
inheritance, with overload resolution always happening in the
base class. The pattern also allows for selective overriding of
functionality and apart from that provides a central entry point
for debugging purposes via the base.

Regards,
Robert
 
A

Alf P. Steinbach

* Robert Fendt -> Sam:
Apart from that, (and I know it does not correspond to your
original question, but you *did* say you are currently learning
C++), you should consider limiting your use of public virtuals
in C++ anyway. However, I will not say (like others do
sometimes) "do not use them at all".

They have their uses, and there's nothing wrong with them if you
use them e.g. to define an abstract interface without inheriting
further from the implementing classes. However, there are
problems that newcomers to C++ often do not expect, especially
if they come from a Java or C# background. The two main sources
of problems are (in my experience):

(1) There is (currently) no 'override' annotation. C++ 0x *will*
have it, but it will probably still take several years before
usable implementations actually become available. In current
standard C++, you can inadvertently create a new function
instead of overriding the base definition without realising it.

This is a test case for a macro CPPX_IS_OVERRIDE_OF:


<code>
class Base
{
protected:
virtual void foo( int, char const* ) const = 0;
};

class Derived:
public Base
{
protected:
virtual void foo( int a, char const* b ) const
{
CPPX_IS_OVERRIDE_OF( Base::foo,(a, b) ); // Fine.
}

virtual void bar() const
{
#if defined( CERR_NONEXISTING )
CPPX_IS_OVERRIDE_OF( Base::bar,() ); // Compilation error.
#endif
}
};
</code>


The macro does not guarantee to detect that you're not overriding. It's just a
code documentation tool, documenting your intention, with a *best effort*
checking that in most cases will detect any problem. It's defined like this
(modulo namespaces and header file packaging, I removed that for this posting):


<code>
template< typename T >
inline void suppressUnusedWarning( T const& ) {}

// The following macro is comment-like, but performs such checking as it can.
#define CPPX_IS_OVERRIDE_OF( memberSpec, args ) \
suppressUnusedWarning( sizeof( (memberSpec args, 0) ) )
</code>


Use of a macro like this is one argument in favor of generally not making
virtuals 'private', but rather make them 'protected', at least by default.

Until I wrote that macro, some years ago, I strongly disagreed with the FAQ's
recommendation of 'protected'; now I hail Marshall Cline for his foresight! :)

(2) Virtual functions and function overloading are a dangerous
mixture in C++. Consider two virtual functions which differ
only by their signatures: now overriding just one in a derived
class actually hides the other if you do not explicitly
instruct the compiler to do otherwise. This is especially
irksome in conjunction with (1).

To help at least with point (2), the "non-virtual interface
pattern" has gained some popularity. In the interface class, the
public (possibly overloaded) functions are declared and defined
as non-virtual functions which in turn call non-overloaded,
private, purely virtual functions. This separates overloading and
inheritance, with overload resolution always happening in the
base class. The pattern also allows for selective overriding of
functionality and apart from that provides a central entry point
for debugging purposes via the base.

Thank you. I never thought of it this way. It's very clarifying. Another reason
is checking of preconditions and postconditions. If only C++ had DBC support...


Cheers,

- Alf
 
S

subramanian100in

* Robert Fendt said:
(1) There is (currently) no 'override' annotation. C++ 0x *will*
have it, but it will probably still take several years before
usable implementations actually become available. In current
standard C++, you can inadvertently create a new function
instead of overriding the base definition without realising it.

(2) Virtual functions and function overloading are a dangerous
mixture in C++. Consider two virtual functions which differ
only by their signatures: now overriding just one in a derived
class actually hides the other if you do not explicitly
instruct the compiler to do otherwise. This is especially
irksome in conjunction with (1).


Regards,
Robert

Kindly give sample programs for (1) and (2) above, so that I can
understand still better.

Thanks
V.Subramanian
 
R

Robert Fendt

Kindly give sample programs for (1) and (2) above, so that I can
understand still better.

(1)

#include <iostream>

class Base
{
public:
void foo(unsigned int x) {std::cout << "Definition #1: " << x << std::endl;}
};

class Deriv: public Base
{
public:
void foo(int x) {std::cout << "Definition #2: " << x << std::endl;}
};

int main()
{
Deriv d;
Base* p = &d;

d.foo(0); // calls Deriv::foo
p->foo(1); // calls Base::foo
}

The function signatures differ slightly, and thus a new/second
definition is created without overriding the old one. To make a
bad thing worse, the new definition also hides the old one (see
also point (2)). If you call foo() directly, you will always get
Derived::foo, while when calling through a base pointer or
reference, Derived::foo will *never* be called. This is almost
never what one wants.

(2)

#include <iostream>

class Base
{
public:
virtual void foo(double x) {std::cout << "Base::foo(double) " << x << std::endl;}
virtual void foo(int x) {std::cout << "Base::foo(int) " << x << std::endl;}
};

class Deriv: public Base
{
public:
void foo(double x) {std::cout << "Deriv::foo(double) " << x << std::endl;}
};

int main()
{
Deriv d;
Base* p = &d;

p->foo(1.0); // calls Deriv::foo(double)
p->foo(1); // calls Base::foo(int)
d.foo(1.0); // calls Deriv::foo(double)
d.foo(1); // calls Deriv::foo(double)
}

The declaration of foo(int) is hidden in the derived class.
Therefore, overload resolution works differently whether you
call foo() directly or via a base pointer (or reference). This
is almost never what one wants.


Regards,
Robert
 
Ö

Öö Tiib

* Robert Fendt -> Sam:








This is a test case for a macro CPPX_IS_OVERRIDE_OF:

<code>
class Base
{
protected:
     virtual void foo( int, char const* ) const = 0;

};

class Derived:
     public Base
{
protected:
     virtual void foo( int a, char const* b ) const
     {
         CPPX_IS_OVERRIDE_OF( Base::foo,(a, b) );  // Fine.
     }

     virtual void bar() const
     {
         #if defined( CERR_NONEXISTING )
             CPPX_IS_OVERRIDE_OF( Base::bar,() );  // Compilation error.
         #endif
     }};

</code>

The macro does not guarantee to detect that you're not overriding. It's just a
code documentation tool, documenting your intention, with a *best effort*
checking that in most cases will detect any problem. It's defined like this
(modulo namespaces and header file packaging, I removed that for this posting):

<code>
     template< typename T >
     inline void suppressUnusedWarning( T const& ) {}

// The following macro is comment-like, but performs such checking as it can.
#define CPPX_IS_OVERRIDE_OF( memberSpec, args ) \
     suppressUnusedWarning( sizeof( (memberSpec args, 0) ) )
</code>

Use of a macro like this is one argument in favor of generally not making
virtuals 'private', but rather make them 'protected', at least by default..

Until I wrote that macro, some years ago, I strongly disagreed with the FAQ's
recommendation of 'protected'; now I hail Marshall Cline for his foresight! :)

Note, that Microsoft allows "override", "sealed" and "abstract" even
in non-managed native code projects as Microsoft extensions to C++.
This causes lots of grievance among people who stand on fact that such
code should not compile.
However if to wrap the keywords into macros that keep things compiling
everywhere then, especially "override" helps to discover lot of
refactoring bugs with Microsoft compiler.

For mitigating the issue (2) that Robert described one needs somewhat
more expensive static code analysis tools and tricks.
 
M

Michael Tsang

Andy said:
Öö Tiib wrote:
Ah! I didn't know that. That explains the fun I had last week. I
overrode a function, and without thinking stuck const on the parameters
it wasn't modifying. Of course the base class called its function, not
the over-ridden one - and the effects were subtle enough that I didn't
realise. It turns out that MS do have a warning for this - but it's a
"level 4" warning, and the MS libraries are _full_ of code that
generates L4 warnings, so we don't use it.

Will you use the Microsoft compiler without using Microsoft libraries or is
there a flag for shut off warnings from system libraries?
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top