仅通过公共继承暴露某些方法

时间:2016-09-17 04:41:16

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

我有一个基类,比如说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()为例,可以由子类和父类调用。但是由于Designer和Manager已经分配了他们的部门,我不希望他们访问这个功能。

我尝试了一种非常愚蠢的方法来解决这个问题,通过编写另一层类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;

那么我的选择是什么?一种愚蠢的方法是将该函数设置为虚拟,但是子类不会被强制覆盖它,如果它们忘记声明它,它会调用父函数吗?

4 个答案:

答案 0 :(得分:3)

  

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

继承基于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都是eval工作顺利。

答案 1 :(得分:2)

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

也许您应该添加另一个派生自Employee的类并将showEveryDept()移动到该类。或者只是简单地允许ManagerDesigner调用showEveryDept()是适当的行为。我不能在不了解你的目标的情况下说出来。

答案 2 :(得分:1)

另一种选择是在子进程中使用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;
        }
};

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

编辑:根据您的评论,我在另一个答案中读到,我可以很容易地得出结论,您的问题是您不理解基类Employee不是用作某种“未分配的员工”占位符。这样:设计师是员工,未分配的员工是员工,但设计师是未分配的员工。因此,最好重新构建代码。无论如何,为了完整起见,我将离开上述解决方案。

答案 3 :(得分:1)

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

实现您所需要的一种方法是对其进行建模:员工是否可以看到所有部门是员工的“属性”。因此,您可以添加bool属性“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;
    }
};