In the following example, I'm trying to hide using Employee::showEveryDept
from the last child class Designer
by making it private in class Elayer
-
#include <iostream>
class Employee {
private:
char name[5] = "abcd";
void allDept() { std::cout << "Woo"; }
public:
void tellName() { std::cout << name << "\n"; }
virtual void showEveryDept()
{
std::cout << "Employee can see every dept\n";
allDept();
}
};
class ELayer : public Employee {
private:
using Employee::showEveryDept;
protected:
ELayer() {}
public:
using Employee::tellName;
};
class Designer : public ELayer {
private:
char color = 'r';
public:
void showOwnDept() { std::cout << "\nDesigner can see own dept\n"; }
};
int main()
{
Employee* E = new Designer;
E->showEveryDept(); // should not work
Designer* D = dynamic_cast<Designer*>(E);
D->showOwnDept();
}
But it is still compiling and the output is -
Employee can see every dept Woo Designer can see own dept
But I've explicitly made it private, see - private: using Employee::showEveryDept;
What am I doing wrong here?
Names of class members have the following properties:
This applies to the names themselves -- not to any variable or function that a name refers to. It is possible to have the same function or variable named by the same name but in a different declarative region.
When a class is inherited, the derived class's declarative region includes all names from the base class; but the access may be changed based on the type of inheritance: although it's only possible to declare a member as public
, protected
, or private
, after inheritance you can end up with a member having no access.
Here is a table of accessability of names and regions in your code:
Note how tellName
is public in all three classes, despite the fact that it was not redeclared in Designer
. Accordingly, the ELayer
's using Employee::tellName;
is redundant because tellName
would have been public
in ELayer
anyway.
The effect of ELayer
's using Employee::showEveryDept;
is that showEveryDept
's access within ELayer
is private
.
Name Lookup is the process of resolving which name-region combination is found by a call to a name. The context of this call includes:
Foo::name
)(*E)
)Access control also takes into account:
For example, looking up showEveryDept
in the context of ELayer
will find the combination ELayer::showEveryDept
with access private
.
But looking up the same name in the context of Employee
will find the combination Employee::showEveryDept
which has access public
.
This behaviour is the same whether or not those two combinations refer to the same function.
Without reproducing the full list of rules about how the calling context translates to which declarative regions are searched, the usage:
`E->showEveryDept`
looks up the name in the region of the static type of *E
, which is Employee
. It does not use the dynamic type, because name lookup is resolved at compile-time. There are no run-time access errors -- access is a compile-time property.
The final step of the access check is to compare public
and Employee
with the call site, which is main()
. The rule is that public
grants access to all call sites, so the access check passes.
virtual-ness does not depend on the properties of names, nor the scope in which the name is being looked up. Unlike access, being virtual is a property of the function, not of any name-region combinations.
When virtual dispatch is active, calling a function will redirect the call to the final overrider of that function.
It's important to keep thinking of this in terms of function implementations - not of names for the functions. Virtual dispatch and Access Control are two completely separate operations.
Virtual dispatch is active only when a virtual function is called by an unqualified-id, which means by naming the function without Bla::
on the front.
So, in your code, E->showEveryDept
does activate virtual dispatch. The access check passes as described above, and then virtual dispatch invokes the final overrider, which happens to be the body defined in Employee
in this example.
In your actual example, virtual
is moot since the function is not overridden. But even if you had overridden showEveryDept
as a private function in ELayer
(instead of the using
declaration), it would still call that function body.