using-declaration不能正常工作

时间:2016-09-17 05:44:42

标签: c++ c++11 inheritance polymorphism using-declaration

在以下示例中,我尝试通过在课程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;

我在这里做错了什么?

5 个答案:

答案 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;
};

但你所做的只是明确introduceshowEveryDept()类到Employee的名称Elayer;引入private访问权限。意思是,我们不能直接访问类成员/静态函数之外与ELayer相关联的名称(或调用该函数)。

ELayer* e = new Designer();
e->showEveryDept();    //Error, the name showEveryDept is private within ELayer

但是,由于showEveryDept()publicElayer的基类中具有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 questionthis

的答案

答案 1 :(得分:5)

声明

E->showEveryDept();

showEveryDept的编译时间知道的类型访问*E。哪个是Employee,可以访问此成员。

答案 2 :(得分:2)

类成员的名称具有以下属性:

  • 名称 - 不合格的标识符。
  • 声明性区域 - 声明名称的类。
  • access - 该区域内名称的权利。

这适用于名称本身 - 而不适用于名称所引用的任何变量或函数。可以使用相同名称但在不同声明区域中命名的相同函数或变量。

当继承类时,派生类的声明性区域包括基类中的所有名称;但访问权限可能会根据继承类型进行更改:虽然只能将成员声明为publicprotectedprivate,但在继承之后,您最终可能会成员无权访问

以下是代码中名称和地区的可访问性表格:

Name accessibility

请注意tellName在所有三个类中是如何公开的,尽管它在Designer中没有重新声明。因此,ELayer的{​​{1}}是多余的,因为using Employee::tellName; tellNamepublic无论如何都是ELayer

ELayer的{​​{1}}的效果是using Employee::showEveryDept; 中的showEveryDept访问ELayer

名称查找是解析通过调用名称找到哪个名称 - 区域组合的过程。此调用的上下文包括:

  • 调用网站,即使用该名称的范围
  • 通话中明确列出的任何范围(例如private
  • 表示正在访问其成员的对象的表达式(例如Foo::name

访问控制也考虑到了:

  • 调用上下文与找到名称的声明性区域之间的关系。

例如,在(*E)的上下文中查找showEveryDept会找到具有访问权限ELayer的组合ELayer::showEveryDept

但在private的上下文中查找相同的名称会找到具有Employee访问权限的组合Employee::showEveryDept

无论这两个组合是否引用相同的功能,此行为都是相同的。

如果没有重现关于调用上下文如何转换为搜索哪些声明性区域的规则的完整列表,则使用:

public

`E->showEveryDept` 静态类型区域中查找名称,即*E。它不使用动态类型,因为名称查找在编译时解析。没有运行时访问错误 - 访问是编译时属性。

访问权限检查的最后一步是将Employeepublic与呼叫网站进行比较,即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