hg_*_*git 12 c++ polymorphism inheritance using-declaration c++11
在下面的例子中,我试图通过在课堂上将其隐藏起来来隐藏using Employee::showEveryDept
最后一个子Designer
类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();
}
Run Code Online (Sandbox Code Playgroud)
但它仍在编译,输出是 -
Run Code Online (Sandbox Code Playgroud)Employee can see every dept Woo Designer can see own dept
但我已明确将其设为私有,请参阅 - private: using Employee::showEveryDept;
我在这做错了什么?
Whi*_*TiM 13
你正在以错误的方式思考它.
C++具有Name Lookup的概念,它是一个很好构建的概念,并不经常出现在我们的脑海中,但这种情况发生在使用名称的任何地方.通过做:
int main()
{
Employee* E = new Designer;
E->showEveryDept(); // should not work
Designer* D = dynamic_cast<Designer*>(E);
D->showOwnDept();
}
Run Code Online (Sandbox Code Playgroud)
该行,E->showEveryDept()
对属于该类的成员(在本例中为成员函数)执行非限定名称查找E
.由于它是一个可访问的名称,该程序是合法的.
我们也知道Designer
是源于ELayer
,你在这里showEveryDept()
声明的private
地方:
class ELayer : public Employee {
private:
using Employee::showEveryDept;
protected:
ELayer() {}
public:
using Employee::tellName;
};
Run Code Online (Sandbox Code Playgroud)
但你刚刚做的是明确地从类中引入名称; 引入访问.意思是,我们不能直接访问与类成员/静态函数外部关联的那个名称(或调用该函数).showEveryDept()
Employee
Elayer
private
ELayer
ELayer* e = new Designer();
e->showEveryDept(); //Error, the name showEveryDept is private within ELayer
Run Code Online (Sandbox Code Playgroud)
然而,由于showEveryDept()
具有public
基类的内访问Elayer
,Employer
; 仍然可以使用限定名称查找来访问它
ELayer* e = new Designer();
e->Employer::showEveryDept(); //Ok, qualified name lookup, showEveryDept is public
Run Code Online (Sandbox Code Playgroud)
Elayer
可访问的名称Designer
将由其访问规范决定.如您所见,名称showEveryDept()
是私有的,所以Designer
甚至不能使用这样的名称.
对于您当前的类层次结构和定义,它意味着,给定:
Employee* E = new Designer();
ELayer* L = new Designer();
Designer* D = new Designer();
Run Code Online (Sandbox Code Playgroud)
- 对于不合格的查找:
这有效
E->showEveryDept(); // works!
Run Code Online (Sandbox Code Playgroud)这失败了:
L->showEveryDept(); // fails; its private in Elayer
Run Code Online (Sandbox Code Playgroud)这也失败了:
D->showEveryDept(); // fails; its inaccessible in Designer
Run Code Online (Sandbox Code Playgroud)- 对于限定查找,在这种情况下,请求从其基类调用此类函数:
这失败了:
D->Elayer::showEveryDept(); // fails! its private in Elayer
Run Code Online (Sandbox Code Playgroud)这有效:
D->Employee::showEveryDept(); // works! its accessible in Employee
Run Code Online (Sandbox Code Playgroud)这也有效:
L->Employee::showEveryDept(); // works! its accessible in Employee
Run Code Online (Sandbox Code Playgroud)请注意:将任何成员函数的名称B::func
(例如)从基类B
引入到派生类中D
,不会覆盖B::func
,它只是 B::func
在派生类的上下文中使重载解析可见.看到这个问题的答案和这个
该声明
E->showEveryDept();
Run Code Online (Sandbox Code Playgroud)
showEveryDept
以编译时已知的类型访问*E
.这是哪个Employee
成员可以访问的地方.
类成员的名称具有以下属性:
这适用于名称本身,而不适用于名称引用的任何变量或函数。可以有相同的函数或变量以相同的名称命名,但位于不同的声明区域中。
当一个类被继承时,派生类的声明区域包括来自基类的所有名称;但访问权限可能会根据继承类型而更改:虽然只能将成员声明为public
、protected
或private
,但继承后您最终可能会得到一个没有访问权限的成员成员。
下面是代码中名称和区域的可访问性表:
请注意tellName
,尽管事实上它并未在Designer
. 因此,ELayer
是using Employee::tellName;
多余的,因为本来tellName
应该是public
ELayer
。
ELayer
's的效果using Employee::showEveryDept;
是is内的showEveryDept
访问。ELayer
private
名称查找是通过调用名称来解析找到哪个名称-区域组合的过程。此次通话的背景包括:
Foo::name
)(*E)
)访问控制还考虑:
例如,showEveryDept
在 的上下文中查找将找到与 access 的ELayer
组合。 ELayer::showEveryDept
private
但是在上下文中查找相同的名称将找到具有访问权限的Employee
组合。Employee::showEveryDept
public
无论这两个组合是否引用相同的函数,此行为都是相同的。
在不复制有关调用上下文如何转换为搜索的声明性区域的完整规则列表的情况下,用法:
`E->showEveryDept`
Run Code Online (Sandbox Code Playgroud)
在 的静态类型区域中查找名称*E
,即Employee
。它不使用动态类型,因为名称查找是在编译时解析的。不存在运行时访问错误——访问是编译时属性。
访问检查的最后一步是将public
和Employee
与调用站点进行比较,即main()
。规则是public
授予对所有调用站点的访问权限,因此访问检查通过。
virtual性不依赖于名称的属性,也不依赖于查找名称的范围。与access不同,虚拟是函数的属性,而不是任何名称区域组合的属性。
当虚拟调度处于活动状态时,调用函数会将调用重定向到该函数的最终重写者。
重要的是要从函数实现的角度来思考这一点,而不是函数的名称。虚拟调度和访问控制是两个完全独立的操作。
仅当虚拟函数被unqualified-id调用时,虚拟调度才处于活动状态,这意味着通过Bla::
在前面不命名该函数。
因此,在您的代码中,E->showEveryDept
确实激活了虚拟调度。如上所述,访问检查通过,然后虚拟调度调用最终的重写器,它恰好是Employee
本示例中定义的主体。
在您的实际示例中,virtual
由于该函数没有被覆盖,因此没有实际意义。但即使您已将其重写showEveryDept
为私有函数ELayer
(而不是using
声明),它仍然会调用该函数体。