Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
The ambiguities we have illustrated with subobjects apply to
any names, including function names. If a class has multiple direct base
classes that share member functions of the same name, and you call one of those
member functions, the compiler doesn t know which one to choose. The following
sample program would report such an error:
//: C09:AmbiguousName.cpp {-xo}
class Top {
public:
virtual ~Top() {}
};
class Left : virtual public Top {
public:
void f() {}
};
class Right : virtual public Top {
public:
void f() {}
};
class Bottom : public Left, public Right {};
int main() {
Bottom b;
b.f(); // Error here
} ///:~
The class Bottom has inherited two functions of the
same name (the signature is irrelevant, since name lookup occurs before
overload resolution), and there is no way to choose between them. The usual
technique to disambiguate the call is to qualify the function call with the
base class name:
//: C09:BreakTie.cpp
class Top {
public:
virtual ~Top() {}
};
class Left : virtual public Top {
public:
void f() {}
};
class Right : virtual public Top {
public:
void f() {}
};
class Bottom : public Left, public Right {
public:
using Left::f;
};
int main() {
Bottom b;
b.f(); // Calls Left::f()
} ///:~
The name Left::f is now found in the scope of Bottom,
so the name Right::f is not even considered. To introduce extra
functionality beyond what Left::f( ) provides, you implement a Bottom::f( )
function that calls Left::f( ).
Functions with the same name occurring in different branches
of a hierarchy often conflict. The following hierarchy has no such problem:
//: C09:Dominance.cpp
class Top {
public:
virtual ~Top() {}
virtual void f() {}
};
class Left : virtual public Top {
public:
void f() {}
};
class Right : virtual public Top {};
class Bottom : public Left, public Right {};
int main() {
Bottom b;
b.f(); // Calls Left::f()
} ///:~
Here, there is no explicit Right::f( ). Since Left::f( )
is the most derived, it is chosen. Why? Well, pretend that Right did not
exist, giving the single-inheritance hierarchy Top <= Left <= Bottom.
You would certainly expect Left::f( ) to be the function called by
the expression b.f( ) because of normal scope rules: a derived
class is considered a nested scope of a base class. In general, a name A::f
dominates the name B::f if A derives from B,
directly or indirectly, or in other words, if A is more derived in the
hierarchy than B. Therefore,
in choosing between two functions with the same name, the compiler chooses the
one that dominates. If there is no dominant name, there is an ambiguity.
The following program further illustrates the dominance
principle:
//: C09:Dominance2.cpp
#include <iostream>
using namespace std;
class A {
public:
virtual ~A() {}
virtual void f() { cout << "A::f\n";
}
};
class B : virtual public A {
public:
void f() { cout << "B::f\n"; }
};
class C : public B {};
class D : public C, virtual public A {};
int main() {
B* p = new D;
p->f(); // Calls B::f()
delete p;
} ///:~
The class diagram for this hierarchy is
The class A is a (direct, in
this case) base class for B, and so the name B::f dominates A::f.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |