为什么C ++允许对公共继承的基本方法进行访问限制?

时间:2016-11-03 16:20:09

标签: c++ inheritance

关于问题" How to publicly inherit from a base class but make some of public methods from the base class private in the derived class?",我有一个跟进问题:

我可以理解,C ++标准允许派生类放宽继承方法的访问限制,但我想不出任何合理的用例,在这里使用访问限制是有意义的派生类。

根据我对继承概念的理解,如果Derived类是公共类Base,那么你可以用Derived完成任何你可以用Base做的事情。如果不希望Derived满足Base的接口,那么首先不应该使用(公共)继承。 (事实上​​,当我在ROOT的TH2 :: Fill(双重)中在野外遇到这种技术时,遗传滥用是一个明显的例子。)

对于虚拟方法,Derived中的访问限制也是无用的,因为Derived的任何用户都可以通过将Derived *强制转换为Base *来使用它们。

因此,从我有限的C ++新手的角度来看,这些限制具有误导性(Derived的程序员可能会认为他的虚拟现在受保护的方法不被其他任何人调用,实际上可能是这样)并且也会混淆[关于公共继承应该暗示什么的我。

是否有一些我遗漏的合法用例?

2 个答案:

答案 0 :(得分:3)

来自Herb Sutter,Guru of the Week #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);
}

答案 1 :(得分:1)

  

根据我对继承概念的理解,如果Derived类是公共类Base,那么你可以用Derived完成任何你可以用Base做的事情。

这不是真正的继承概念;这是多态的概念。特别是,这是Liskov替代原则的陈述。但是继承可以用于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有方法doublehalve。此模式称为奇怪重复模板模式(CRTP)。请注意,我们永远不会实例化Foo<Bar>或指向它。我们只是使用它提供的接口,非多态。

现在,假设某人想要使用Foo,但只想在其界面中使用double,而不是halve。在这种情况下,在导出中使halve为私有是完全有效的。毕竟,派生类只是一些特定的非多态类型,不需要符合作者想要/文档之外的任何接口。

请注意,使用CRTP时,基类通常会提供公共功能,并且您通常会公开继承。 CRTP在这个用例中的全部优点是你可以直接注入接口;如果您要将这些方法设为私有或私下继承,那么最好让Foo<Bar>成为Bar的成员。因此,在这种特殊情况下,将某些事情公之于众是私有的,而不是反过来更为常见。