例如,课程Base
有两种公开方法:foo()
和bar()
。类Derived
继承自类Base
。在课程Derived
中,我想将foo()
公开,bar()
私有。下面的代码是正确而自然的方法吗?
class Base {
public:
void foo();
void bar();
};
class Derived : public Base {
private:
void bar();
};
答案 0 :(得分:82)
C ++ '03标准的第11.3节描述了这种能力:
11.3访问声明
访问权限 基类的成员可以更改 在派生类中提及它 派生类中的qualified-id 宣言。这样的提及被称为 访问声明。一个人的影响 访问声明qualified-id;是 定义为等同于 声明使用qualified-id
所以有两种方法可以做到。
注意:从ISO C ++ '11开始,如注释中所述,禁止访问声明(
Base::bar;
)。应该使用using声明(using Base::bar;
)。
1)您可以使用公共继承,然后将栏设为私有:
class Base {
public:
void foo(){}
void bar(){}
};
class Derived : public Base {
private:
using Base::bar;
};
2)您可以使用私有继承,然后将foo公开:
class Base {
public:
void foo(){}
void bar(){}
};
class Derived : private Base {
public:
using Base::foo;
};
注意:如果你有一个Base类型的指针或引用,它包含Derived类型的对象,那么用户仍然可以调用该成员。
答案 1 :(得分:12)
实际上没有办法做你想做的事,因为如果你从Base
公开派生,那么班级的用户将始终能够:
Derived d;
Base& b = d;
b.bar();
使基类中的类派生publicy中的公共基类函数不可访问是“正确或自然的”;相反,应该重构基类接口,使这些函数不公开或分成单独的类。
答案 2 :(得分:8)
首先,您必须从OOP角度了解您想要做什么。有两种完全不同的继承类型:
接口的继承。当您在Java或其他语言中使用接口作为独立实体执行implement
时,当您从C ++中公开继承空抽象类时,也会发生这种情况。在这里你根本不关心代码,但是想告诉你的编译器和每个使用你的基类/派生类的人,这个派生类是你的基类的特殊类型,它具有所有的属性基类,它的行为完全,就像它的用户可见的基类一样,并且可以在任何算法中用来代替基类。
代码的继承。您在基类中有一段代码要在派生类中重用。基类和派生类不必以任何方式相关,您只想重用代码,就是这样。
C ++中的公共继承是两种类型的混合,您获得了接口继承,并且您也获得了代码继承。私有继承是一种不同类型的野兽,你得到只代码继承,派生类的用户不能使用它代替基类,而且从用户的角度来看基础和派生类没什么关系,所以 - 所以-ever。
struct Base {};
struct PublicDerived : public Base {};
struct PrivateDerived: private Base {};
Base * base; PublicDerived * public_derived; PrivateDerived * private_derived;
base = public_derived; //good
base = private_derived; //compilation error.
由于您想要更改界面,不应该使用公共继承,通过更改界面,您实际上说这两个类具有不同的行为,并且不能互换使用。所以你真正想要的是私下继承,然后制作你想要公开的所有方法,不是相反的方式。
答案 3 :(得分:2)
根据Liskov Substitution Principle,公共继承应该模拟“is-a”。您Derived
公开继承自Base
的内容是,无论何处需要Base
对象,Derived
类型的对象都会这样做。
如果您要求Derived
隐藏一些可用于Base
的操作,那么您建模的内容不是“is-a”,而公共继承不是正确的工具。< / p>
你想要的是私人继承或组合,正如其他答案所详述的那样。
答案 4 :(得分:1)
假设你有这个:
class Foo{
public:
void method1();
void method2();
void notGonnaSeeIt();
private:
//stuff
};
要有效地包装它,您可以执行私有继承并将您想要的方法传递给Brian建议的公共声明:
class Bar : private Foo{
void methodA(){ method1(); }
void methodB(){ method2(); }
//more stuff
};
或者你可以用装饰器包装它
template<class T>
class Bar{
public:
Bar(T *_input) : m_Input(_input){}
void methodA() { m_Input->method1(); }
void methodB() { m_Input->method2(); }
//whatever else you need/want
private:
T* m_Input;
};
就个人而言,我更喜欢模板方式,因为它允许你对任何继承自Foo
的类做同样的事情。
答案 5 :(得分:1)
如果以后不需要将其视为基类,只需要基类的某些函数,那么可以使用组合而不是继承? (C#是我的第一语言,但你明白了)
class Base {
public void foo();
public void bar();
};
class Derived {
private Base _base;
public void bar() {
_base.bar();
}
};
答案 6 :(得分:0)
使用C ++ 11,您可以执行以下操作:
class Base {
public:
void foo();
void bar();
};
class Derived : public Base {
private:
using Base::bar;
};
这确保无法从Derived类外部访问Base :: bar()。它仍然可以从Base类外部访问。