在以下示例中,我尝试通过在课程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();
}
但它仍在编译,输出是 -
Employee can see every dept Woo Designer can see own dept
但我已明确将其设为私有,请参阅 - private: using Employee::showEveryDept;
我在这里做错了什么?
答案 0 :(得分:13)
你正在以错误的方式思考它。
C ++具有Name Lookup的概念,它是一个很好构建的概念,我们不会经常想到它,但这种情况在使用name的任何地方都会发生。通过做:
int main()
{
Employee* E = new Designer;
E->showEveryDept(); // should not work
Designer* D = dynamic_cast<Designer*>(E);
D->showOwnDept();
}
行E->showEveryDept()
对属于E
类的成员(在本例中为成员函数)执行非限定名称查找。由于它是accessible name,因此该程序是合法的。
我们也知道Designer
源于ELayer
,其中showEveryDept()
声明为private
,就像您在此处所做的那样:
class ELayer : public Employee {
private:
using Employee::showEveryDept;
protected:
ELayer() {}
public:
using Employee::tellName;
};
但你所做的只是明确introduce从showEveryDept()
类到Employee
的名称Elayer
;引入private
访问权限。意思是,我们不能直接访问类成员/静态函数之外与ELayer
相关联的名称(或调用该函数)。
ELayer* e = new Designer();
e->showEveryDept(); //Error, the name showEveryDept is private within ELayer
但是,由于showEveryDept()
在public
,Elayer
的基类中具有Employer
访问权限,它仍然可以使用qualified name lookup
ELayer* e = new Designer();
e->Employer::showEveryDept(); //Ok, qualified name lookup, showEveryDept is public
Elayer
中可访问的Designer
名称将由其access specification决定。如您所见,名称showEveryDept()
是私有的,因此Designer
甚至无法使用此名称。
对于您当前的类层次结构和定义,它意味着,给定:
Employee* E = new Designer();
ELayer* L = new Designer();
Designer* D = new Designer();
- 适用于unqualified lookup:
这可行
E->showEveryDept(); // works!
这失败了:
L->showEveryDept(); // fails; its private in Elayer
这也失败了:
D->showEveryDept(); // fails; its inaccessible in Designer
- 对于qualified lookup,在这种情况下,请求从其基类调用此类函数:
这失败了:
D->Elayer::showEveryDept(); // fails! its private in Elayer
这有效:
D->Employee::showEveryDept(); // works! its accessible in Employee
这也有效:
L->Employee::showEveryDept(); // works! its accessible in Employee
请注意:将任何成员函数的名称B::func
(例如)从基类B
引入派生类D
,不会覆盖{{1} },它只是使B::func
在派生类的上下文中重载解析可见。查看this question和this
答案 1 :(得分:5)
声明
E->showEveryDept();
以showEveryDept
的编译时间知道的类型访问*E
。哪个是Employee
,可以访问此成员。
答案 2 :(得分:2)
类成员的名称具有以下属性:
这适用于名称本身 - 而不适用于名称所引用的任何变量或函数。可以使用相同名称但在不同声明区域中命名的相同函数或变量。
当继承类时,派生类的声明性区域包括基类中的所有名称;但访问权限可能会根据继承类型进行更改:虽然只能将成员声明为public
,protected
或private
,但在继承之后,您最终可能会成员无权访问。
以下是代码中名称和地区的可访问性表格:
请注意tellName
在所有三个类中是如何公开的,尽管它在Designer
中没有重新声明。因此,ELayer
的{{1}}是多余的,因为using Employee::tellName;
tellName
中public
无论如何都是ELayer
。
ELayer
的{{1}}的效果是using Employee::showEveryDept;
中的showEveryDept
访问为ELayer
。
名称查找是解析通过调用名称找到哪个名称 - 区域组合的过程。此调用的上下文包括:
private
)Foo::name
)访问控制也考虑到了:
例如,在(*E)
的上下文中查找showEveryDept
会找到具有访问权限ELayer
的组合ELayer::showEveryDept
。
但在private
的上下文中查找相同的名称会找到具有Employee
访问权限的组合Employee::showEveryDept
。
无论这两个组合是否引用相同的功能,此行为都是相同的。
如果没有重现关于调用上下文如何转换为搜索哪些声明性区域的规则的完整列表,则使用:
public
在`E->showEveryDept`
的静态类型区域中查找名称,即*E
。它不使用动态类型,因为名称查找在编译时解析。没有运行时访问错误 - 访问是编译时属性。
访问权限检查的最后一步是将Employee
和public
与呼叫网站进行比较,即Employee
。规则是main()
授予对所有呼叫站点的访问权限,因此访问权限检查通过。
virtual -ness不依赖于名称的属性,也不依赖于查找名称的范围。与 access 不同,虚拟是函数的属性,而不是任何名称 - 区域组合。
当虚拟调度处于活动状态时,调用函数会将调用重定向到该函数的最终覆盖。
在功能实现方面继续考虑这一点非常重要 - 而不是功能名称。虚拟调度和访问控制是两个完全独立的操作。
仅当 unqualified-id 调用虚函数时,虚拟调度才处于活动状态,这意味着通过在前面命名没有public
的函数。
因此,在您的代码中,Bla::
会激活虚拟调度。访问检查如上所述通过,然后虚拟分派调用最终的覆盖,在本例中恰好是E->showEveryDept
中定义的主体。
在您的实际示例中,Employee
没有实际意义,因为函数未被覆盖。但即使您在virtual
(而不是showEveryDept
声明)中将ELayer
覆盖为私有函数,它仍会调用该函数体。
答案 3 :(得分:0)
在main()
函数中,明确您的类型并将其称为 -
int main()
{
Employee* E = new Designer;
E->showEveryDept(); // will work
Designer* D = dynamic_cast<Designer*>(E);
D->showOwnDept();
D->showEveryDept(); // <-- Not legal now
}
会产生错误 -
prog.cc: In function 'int main()': prog.cc:28:22: error: 'virtual void Employee::showEveryDept()' is inaccessible within this context D->showEveryDept(); ^ prog.cc:8:26: note: declared here virtual void showEveryDept(){std::cout<< "Employee can see every dept\n";
答案 4 :(得分:0)
我在这里做错了什么?
你没有做错任何事。*
预期结果是错误的:
虚拟函数的访问说明符在Employee类型上检查,而不是在Designer上,因为您可能期望=&gt; link
(*)除了将访问规则更改为层次结构上的虚拟方法之外,我认为设计不好=&gt; check this。