为什么c++允许对公共继承基方法的访问限制
Why does C++ allow access restrictions on public inherited base methods?
关于"如何从基类公开继承,但在派生类中使基类的一些公共方法私有?"的问题,我有一个后续问题:
我可以理解c++标准允许派生类放宽继承方法的访问限制,但我想不出任何合法的用例,在派生类中施加访问限制是有意义的。根据我对继承概念的理解,如果类Derived是公共类Base,那么你可以用Base做的任何事情也可以用Derived做。如果不希望派生类实现Base类的接口,那么首先就不应该使用(公共)继承。(事实上,当我在ROOT的TH2::Fill(double)中遇到这种技术时,这是一个明显的继承滥用案例。)
对于虚拟方法,Derived中的访问限制也是无用的,因为派生的任何用户都可以通过将派生*转换为Base*来使用它们。
因此,从我有限的c++新手的角度来看,这些限制是误导的(派生的程序员可能会假设他的虚拟现在受保护的方法不会被任何人调用,而实际上它可能会),并且还使我对公共继承应该意味着什么感到困惑。
是否有一些合法的用例我错过了?
选自Herb Sutter,本周导师#18:
准则#3:只有当派生类需要调用虚函数的基类实现时,才使虚函数受保护。
关于详细的答案,请阅读我在下面代码中的评论:
#include <iostream>
#include <typeinfo>
#include <memory>
struct Ultimate_base {
virtual ~Ultimate_base() {}
void foo() { do_foo(); }
protected:
// Make this method protected, so the derived class of this class
// can invoke this implementation
virtual void do_foo() { std::cout << "Ultimate_base::foo"; }
};
struct Our_derived : Ultimate_base {
private:
// Make this method private, so the derived class of this class
// can't invoke this implementation
void do_foo() {
Ultimate_base::do_foo();
std::cout << " Our_derived::foo";
}
};
struct Derive_from_derive : Our_derived {
private:
void do_foo() {
// You can't call below code
// vvvvvvvvvvvvvvvvvvvvvv
// Our_derived::do_foo();
std::cout << " Derive_from_derive::foo";
}
};
// This class is marked final by making its destructor private
// of course, from C++11, you have new keyword final
struct Derive_with_private_dtor : Ultimate_base {
private:
~Derive_with_private_dtor() {}
};
// You can't have below class because its destructor needs to invoke
// its direct base class destructor
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
/*
struct Derive_from_private_dtor : Derive_with_private_dtor {
};
*/
int main() {
std::unique_ptr<Ultimate_base> p = std::make_unique<Our_derived>();
p->foo();
p = std::make_unique<Derive_from_derive>();
p->foo();
p.reset(new Derive_with_private_dtor);
}
根据我对继承概念的理解,如果类Derived是公共类Base,那么你可以用Base做的任何事情也可以用Derived做。
这不是继承的真正概念;这就是多态性的概念。特别地,这是一个利斯科夫替代原理的陈述。但是在c++中,继承可以用于多种用途,而不仅仅是多态性。实际上,只要基类具有非虚方法或数据成员,就可以使用它将实现或状态注入派生类,而不仅仅是为了多态。如果基类没有虚方法和虚析构函数,则不应该(不能)以多态方式使用该类。您可以使用既不会实例化基类,也不会使用指向基类的指针的基类。下面是一个例子:
template <class T>
struct Foo {
T double_this() { return 2 * static_cast<T&>(*this).data; }
T halve_this() { return 0.5 * static_cast<T&>(*this).data; }
};
这个奇怪的类是做什么的?好吧,它可以用来注入一些接口到任何类的数据成员称为data
(和一个适当的构造函数):
struct Bar : Foo<Bar> {
Bar(double x) : data(x) {}
double data;
};
现在Bar
有double
和halve
方法。这种模式称为奇怪循环模板模式(CRTP)。请注意,我们永远不会实例化Foo<Bar>
或拥有指向它的指针。我们只使用它提供的接口,非多态的。
现在,假设有人想使用Foo
,但只希望double
在他们的接口,而不是halve
。在这种情况下,在派生类中将halve
设置为私有是完全有效的。毕竟,派生类只是一些特殊的、非多态的类型,除了作者想要的/文档之外,它不需要遵守任何接口。
注意,当使用CRTP时,基类通常会提供公共函数,并且您通常会公开继承。在这个用例中,CRTP的全部优点是您可以直接注入接口;如果您打算将方法设为私有或私有继承,那么最好将Foo<Bar>
设为Bar
的成员。因此,在这种特殊情况下,更常见的是将公共内容变为私有内容,而不是相反。
- 通过方法访问结构
- 模板方法访问正向声明的类仅在没有此指针的情况下无法编译
- 类变量无法从类方法访问
- 为什么从静态方法访问非静态方法是糟糕的设计
- 子类是否也在 c++ 中继承私有数据成员?但通过超类的公共方法访问
- 如何从回调方法访问自定义类成员
- 使初始化变量可供多个属性初始化方法访问
- 从c++中的方法访问指向对象的专用指针数组
- C++如何通过回调方法访问成员变量
- "undefined reference"从静态方法访问的模板类的静态成员
- 如何从方法访问类成员
- 类静态方法访问其静态数据成员
- 从静态方法访问非静态成员的工作示例
- 类方法访问其数据成员
- 通过公共方法访问私有成员变量
- 为什么无法从模板方法访问此类自己的受保护成员?
- 通过非静态方法访问静态成员
- JNI保留对对象的全局引用,并使用其他JNI方法访问它.在多个JNI调用中保持C++对象的活动状态
- 如何让一个方法访问其他模板类实例的私有成员
- 方法访问C++中的对象实例化