【问题标题】:exposing only certain methods through public inheritence通过公共继承仅公开某些方法
【发布时间】:2017-01-25 08:34:45
【问题描述】:

我有一个基类,比如Employee,上面有一些方法。稍后我将派生一些子类,例如ManagerDeveloperDesigner 等,它们也是雇员(因为继承)。现在说代码看起来像 -

#include <iostream>
#include <vector>

class Employee{
    private : char name[5] = "abcd";
              void allDept(){ std::cout<<"Woo"; }

    public: void tellName(){std::cout << name << "\n"; }
            void showEveryDept(){std::cout<< "Employee can see every dept\n"; 
                                 allDept(); }
            virtual ~Employee() {}
};

class Manager: public Employee{
    private : char dept[5] = "aaaa";
    public: void showOwnDept(){std::cout<< "Manager can see own dept\n";}
};

class Designer: public Employee{
    private : char color = 'r';
    public: void showOwnDept(){std::cout<< "Designer can see own dept\n";}
};

int main(){

    Employee *E = new Designer;

    E->showEveryDept();

    // E->showOwnDept(); // will not work, but can be casted dynamically and even statically if sure, to call it!

    Designer* D = dynamic_cast<Designer*>(E);

    D->showOwnDept();
}

所以我们在这里可以看到,我可以强制转换它并使用多态性,将基类指针指向派生类对象,并且仍然在子类上调用基类可访问方法。还要从子类中调用子类方法,我可以动态地把它回滚,对吧。

但是现在我想做的是,从子类调用中隐藏一个公共类成员,这样子类就不能调用它,但基类对象可以。以showEveryDept() 为例,子类和父类都可以调用它。但是由于设计师和经理已经分配了他们的部门,我不希望他们访问这个功能。

我尝试了一种非常老套的方法来解决这个问题,通过编写另一层 b/w Employee 类和它的子类,就像这样 -

class Employee{
    private : char name[5] = "abcd";
              void allDept(){ std::cout<<"Woo"; }

    public: void tellName(){std::cout << name << "\n"; }
            void showEveryDept(){std::cout<< "Employee can see every dept\n";
                                 allDept();}
            virtual ~Employee() {}
};

class ELayer: private Employee{
    private: using Employee::showEveryDept;
    private: using Employee::tellName;
};

class Manager: public ELayer{
    private : char dept[5] = "aaaa";
    public: void showOwnDept(){std::cout<< "Manager can see own dept\n";}
};

class Designer: public ELayer{
    private : char color = 'r';
    public: void showOwnDept(){std::cout<< "Designer can see own dept\n";}
};

int main(){
    Employee *E = new Designer;
    E->showEveryDept();
    // E->showOwnDept(); // will not work, but can be casted dynamically
                      // and even statically if sure, to call it!
    Designer* D = dynamic_cast<Designer*>(E);
    D->showOwnDept();
}

但看起来很聪明,但它不起作用 -

prog.cc: In function 'int main()':
prog.cc:27:23: error: 'Employee' is an inaccessible base of 'Designer'
     Employee *E = new Designer;

那么我有哪些选择呢?一种愚蠢的方法是将该函数设置为 virtual ,但子类又不会被强制覆盖它,如果他们忘记声明它,它会调用父类的函数?

【问题讨论】:

    标签: c++ class c++11 inheritance polymorphism


    【解决方案1】:

    但现在我想做的是,从子类调用中隐藏一个公共类成员,这样子类就不能调用它,但基类对象可以。

    继承基于Liskov substitution principle。简而言之,在我使用Base* 的任何地方,我都应该能够使用Derived*,并且一切都应该同样有效。你想违反这个概念,通过为你的派生类制作一个格式错误的基类操作。这意味着您的抽象是错误的。

    而且,这样的事情无论如何都是没有意义的。你不能动态实现这样的机制,如果你静态实现它,那么:

    Derived d;
    Base* b = &d;
    
    b->foo(); // OK
    d.foo();  // error
    

    我总是可以这样做:

    static_cast<Base&>(d).foo(); // OK, just verbose
    

    您可能想要层次结构的第二个分支:

    struct Base { };
    struct SpecialDerived : Base { void foo(); };
    struct NormalDerived : Base { };
    

    现在只有SpecialDerived 可以调用foo(),但每个SpecialDerived 仍然是一个Base,每个NormalDerived 都是一个Base,并且一切正常。

    【讨论】:

      【解决方案2】:

      在我看来,您可能需要重新考虑一下您的继承层次结构。如果从Employee 派生的类不能调用showEveryDept(),那么这向我表明showEveryDept() 不应该是Employee 的一部分。通过尝试从其派生类中的Employee 公共接口中删除方法,您正在破坏子类型关系。如果B 缺少使某事物成为A 的某些行为,那么B 不是 A 并且不应派生自A

      也许您应该添加另一个派生自Employee 的类并将showEveryDept() 移动到该类。或者简单地允许ManagerDesigner 调用showEveryDept() 是适当的行为。我不能说不知道更多关于你的目标。

      【讨论】:

      • 看到我不能仅仅因为这种语言不允许这种模型,就将现实世界的关系改造成人为的东西。员工在没有分配某事时也可以看到其他部门,但是一旦他们被继承进入某个部门。 ,他们可以看到自己的部门。只要。也许我缺乏这门语言的专业知识,但要求只是这些。这就是我寻求帮助以使其正常工作的原因。
      • “也许您应该添加另一个派生自 Employee 的类并将 showEveryDept() 移至该类。”正确,我实际上已经尝试过,甚至在我的答案中也写过,但效果不佳:/
      • 考虑到这些限制,我将重新构建您的层次结构以包含类似“UnassignedEmployee”的内容,源自Employee。然后将您的 showEveryDept() 方法放入该类中。编辑:我的建议和你已经尝试过的区别在于 ManagerDesigner 不应派生自 UnassignedEmployee (或任何你称之为的)。
      【解决方案3】:

      另一种选择是在子级中使用using 声明以及私有继承,以选择性地决定您可以从中访问什么。这比virtual 替代方案更灵活,并且没有任何额外的开销。此外,它还可以将公共访问“转换”为受保护的访问。

      class Employee
      {
          private:
              char name[5] = "abcd";
      
              void allDept()
              {
                  std::cout << "Woo";
              }
      
          public:
              void tellName()
              {
                  std::cout << name << "\n";
              }
      
              void showEveryDept()
              {
                  std::cout << "Employee can see every dept\n";
                  allDept();
              }
      
              virtual ~Employee() {}
      };
      
      class Designer : private Employee
      {
          private:
              char color = 'r';
      
          public:
              using Employee::tellName();
      
              void showOwnDept()
              {
                  std::cout<< "Designer can see own dept\n";
              }
      };
      

      现在,您可以致电Desginer::tellName()Designer::showOwnDept(),但Designer::showEveryDept() 是私密的!但是,缺点是您可能不再将外部代码中的Designer* 转换为Employee*。您可以在Employee 中添加一个方法来做到这一点。但是,您应该记住在派生类中使用using Employee::as_employee

      class Employee
      {
          public:
              Employee& as_employee()
              {
                  return *this;
              }
      
              const Employee& as_employee() const
              {
                  return *this;
              }
      };
      

      无论如何,你应该问问自己这是否真的是最好的设计并且你真的需要这样做,或者如果在派生的Employee 中有一个(可选纯)虚函数showDept() 会更好类可能(或必须,如果纯粹的话)覆盖。

      编辑:从我在另一个答案中读到的您的评论中,我可以很容易地得出结论,您的问题是您不理解基类 Employee 不能用作某种“未分配的员工”占位符。这么说吧:设计师是员工,未分配的员工是员工,但设计师不是未分配的员工。所以,最好重构你的代码。无论如何,出于完整性的目的,我将保留上述解决方案。

      【讨论】:

        【解决方案4】:

        在子级中隐藏父类的方法(直接或间接)并不是解决此问题的最佳方法。例如,如果孩子的孩子想要找回隐藏的功能怎么办?真是一团糟。

        实现您需要的一种方法是对其进行建模:员工能否看到所有部门是员工的“属性”。因此,您可以添加一个布尔属性“CanShowEveryDept”,根据您的功能需求在父类中确定其默认值,并在每个子类构造函数中正确设置。

        class Employee
        {
        protected:
            bool CanShowEveryDept;
        public:
            Employee()
            {
                CanShowEveryDept = true;
            }
        
        
        public:
            void showEveryDept()
            {
                if (!CanShowEveryDept)
                    return;
        
                std::cout << "Employee can see every dept\n";
                allDept();
            }
        };
        
        class Designer : private Employee
        {
        public:
            Designer()
            {
                CanShowEveryDept = false;
            }
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-20
          • 2020-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-13
          • 2011-01-05
          • 2014-11-14
          相关资源
          最近更新 更多