我有一个基类,比如说Employee
有一些方法。我稍后会推导出一些子类,如Manager
,Developer
,Designer
等,这些子类也是员工(因为继承)。现在说代码看起来像 -
#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;
那么我的选择是什么?一种愚蠢的方法是将该函数设置为虚拟,但是子类不会被强制覆盖它,如果它们忘记声明它,它会调用父函数吗?
答案 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()
移动到该类。或者只是简单地允许Manager
和Designer
调用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;
}
};