【问题标题】:How to publicly inherit from a base class but make some of public methods from the base class private in the derived class?如何从基类公开继承,但在派生类中将基类的一些公共方法设为私有?
【发布时间】:2010-06-07 03:16:17
【问题描述】:

例如,Base 类有两个公共方法:foo()bar()。类Derived 继承自类Base。在课堂Derived 中,我想将foo() 设为公开但bar() 设为私有。以下代码是正确且自然的方法吗?

class Base {
   public:
     void foo();
     void bar();
};

class Derived : public Base {
   private:
     void bar();
};

【问题讨论】:

    标签: c++


    【解决方案1】:

    C++ '03 标准的第 11.3 节描述了这种能力:

    11.3 访问声明
    一个访问 基类的成员可以更改 在派生类中通过提及其 派生类中的限定 ID 宣言。这种提及被称为 访问声明。一个效果 访问声明qualified-id;是 定义为等价于 使用qualified-id声明

    所以有两种方法可以做到。

    注意:从 ISO C++ '11 开始,禁止访问声明 (Base::bar;),如 cmets 中所述。应该使用 using 声明 (using Base::bar;)。

    1) 您可以使用公共继承,然后将 bar 设为私有:

    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:可以,但它是为了授予访问权限,而不是限制它。
    • 我选择这个答案作为接受的答案只是从语言特性方面,而不是从 OOD 最佳实践方面。
    • -1 用于使用已弃用的Base::foo 而不是using Base::foo
    • Base::foo 在 C++ 03 中被弃用了吗?如果它在 C++ 11 上被弃用,我不理解 -1,因为此时的答案是正确的。
    • 访问声明是否会阻止我们将派生类实例的指针/引用转换为基类实例,然后访问“隐藏”成员?我怀疑这是该功能旨在涵盖的用例,但我仍然很好奇。
    【解决方案2】:

    真的没有办法做你想做的事,因为如果你公开派生自 Base,该类的用户将始终能够:

    Derived d;
    Base& b = d;
    b.bar();
    

    在从基类派生的 publicy 类中使​​公共基类函数不可访问是不“正确或自然的”;相反,应重构基类接口,使这些函数不公开或拆分为单独的类。

    【讨论】:

    • -1 因为你断言它不能完成是错误的,但 +1 因为你断言它可能不应该完成是正确的
    • 逆向如何——通过私有访问派生,然后将所需函数公开?
    • @jamesMcNellis 对不起,我听不懂你在说什么,意味着Brian R. Bondy 给出的答案是错误的?请你给我解释一下。我是学生,是C++ 的新手。
    【解决方案3】:

    首先,您必须从 OOP 的角度了解您想要做什么。有两种完全不同的继承类型:

    1. 接口的继承。当您在 Java 或其他将接口作为独立实体的语言中执行 implement 时,当您从 C++ 中的空抽象类公开继承时也会发生这种情况。在这里,您根本不关心代码,而是想告诉您的编译器和使用您的基类/派生类的每个人,这个派生类是一种特殊的基类,它具有 所有 的属性基类,它的行为完全与基类的行为在其用户可见的范围内,并且可以在任何算法中代替基类使用。

    2. 代码的继承。您在基类中有一段代码要在派生类中重用。基类和派生类不必有任何关联,你只想重用代码就可以了。

    C++ 中的公共继承是两种类型的混合,您可以获得接口继承和代码继承。私有继承是另一种野兽,你得到only代码继承,你的派生类的用户不能使用它来代替基类,从用户的角度来看,基类和派生类没有关系what-so -永远。

    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.
    

    既然你想改变接口,你不应该去使用公共继承,通过改变接口你实际上说这两个类有不同的行为,不能互换使用。所以你真正想要的是私有继承,然后将你想要的所有方法公开,而不是相反

    【讨论】:

      【解决方案4】:

      根据Liskov Substitution Principle,公共继承应该建模“is-a”。您对 Derived 公开继承自 Base 的意思是,在需要 Base 对象的任何地方,Derived 类型的对象都可以。

      如果您要求Derived 隐藏一些可用于Base 的操作,那么您正在建模的东西不是“is-a”,并且公共继承不是正确的工具。

      正如其他答案所详述的那样,您想要的是私有继承或组合。

      【讨论】:

        【解决方案5】:

        使用 C++ 11,您可以执行以下操作:

        class Base {
           public:
             void foo();
             void bar();
        };
        
        class Derived : public Base {
           private:
             using Base::bar;  
        
        };
        

        这确保 Base::bar() 不能从 Derived 类外部访问。当然,它仍然可以从 Base 类之外访问。

        【讨论】:

          【解决方案6】:

          假设你有这个:

          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 的任何类执行相同的操作。

          【讨论】:

            【解决方案7】:

            如果以后不需要把它当作基类,只需要基类的一些功能,可以用组合而不是继承? (C# 是我的第一语言,但你懂的)

            class Base {
                public void foo();
                public void bar();
            };
            
            class Derived {
                private Base _base;
            
                public void bar() {
                    _base.bar();
                }
            };
            

            【讨论】:

              猜你喜欢
              • 2019-06-10
              • 1970-01-01
              • 2018-05-19
              • 2016-10-08
              • 2016-07-06
              • 2021-11-15
              • 1970-01-01
              • 2020-09-11
              • 2017-04-14
              相关资源
              最近更新 更多