Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Virtual function being hidden

Tags:

I'm having a problem with C++ inheritance.

I have a class hierarchy:

class A { public:    virtual void onFoo() {}    virtual void onFoo(int i) {} };  class B : public A { public:     virtual void onFoo(int i) {} };  class C : public B { };   int main() {     C* c = new C();     c->onFoo(); //Compile error - doesn't exist } 

My question is: why doesn't this compile? My understanding is that C should inherit both onFoo functions from A - and in fact, this compiles if you remove the redefinition of onFoo in B - but g++ gives an error that C has no onFoo() function.

like image 661
Nathaniel Flath Avatar asked Jul 17 '11 22:07

Nathaniel Flath


People also ask

Do virtual functions have to be public?

virtual functions can be private . This is because private means that the function cannot be called by derived classes. It does not prevent the entry to the v-table being overwritten. This means that the both the base class and the derived class will have access to the overwritten virtual function.

Can a virtual function be private?

A virtual function can be private as C++ has access control, but not visibility control. As mentioned virtual functions can be overridden by the derived class but under all circumstances will only be called within the base class.

Should virtual functions be private?

Virtual methods, in their view, should never be public, because they define the class' interface, which must remain consistent in all derived classes. Protected and private virtuals define the class' customizable behavior, and there is no need to make them public.

Is it necessary to override every virtual function?

The virtual keyword can be used when declaring overriding functions in a derived class, but it is unnecessary; overrides of virtual functions are always virtual. Virtual functions in a base class must be defined unless they are declared using the pure-specifier.


2 Answers

The issue that you are experiencing is related to how name lookup works in C++. In particular, when resolving a member, the compiler will look into the static type of the object on which the member is being accessed. If the identifier is found in that class, then lookup completes and (in the case of member functions) overload resolution starts. If the identifier is not found, it will crawl up the hierarchy, class by class, trying to locate the identifier one level at a time.

In your particular case, you have c->onFoo(); and c is of type C. The compiler does not see any declaration of onFoo in C, so it continues upwards in the hierarchy. When the compiler checks B, it sees that there is a declaration of void onFoo(int i) at that level, so it stops lookup, and tries overload resolution. At this time, the overload resolution fails due to the inconsistency in the arguments.

The fact that a declaration of void onFoo(int) is present at B level has the effect of hiding the rest of the overloads in any base class, as it will stop lookup. Note that this is a problem with unqualified lookup, the function is still there and applicable to the object, but will not be found by regular lookup (you can still call it as c->A::onFoo()).

As of how to deal with hiding, the simplest way is by employing the using declaration to bring the functions into scope:

class B : A { public:    using A::onFoo; // All A::onFoo overloads are *considered* here    void onFoo( int ); }; 

The effect of the using declaration here is that when the B class is looked up, in search for the onFoo identifier, the compiler is instructed to also consider all overloads of onFoo in the base class, enabling regular lookup to find A::onFoo().

like image 108
David Rodríguez - dribeas Avatar answered Nov 23 '22 23:11

David Rodríguez - dribeas


If you want base class members to overload derived class members, you want to use using:

struct A {    virtual void onFoo() {}    virtual void onFoo(int i) {} };  struct B : A {     using A::onFoo;     virtual void onFoo(int i) {} };  struct C : B { };   int main() {     C* c = new C();     c->onFoo(); } 
like image 44
Ben Voigt Avatar answered Nov 23 '22 23:11

Ben Voigt