【问题标题】:a way in c++ to hide a specific functionc++中隐藏特定函数的一种方法
【发布时间】:2011-06-21 23:12:37
【问题描述】:

我有一个继承struct A : public B,我想对B隐藏个别功能,这可能吗?

我知道在 A 声明中使用 using BMethod 是可能的。

干杯

【问题讨论】:

  • 你想通过这种隐藏达到什么效果?

标签: c++ inheritance using private-members public-fields


【解决方案1】:

如果您想有选择地对 B 隐藏函数,那么首先使用公共继承没有多大意义。
使用私有继承并有选择地将 B 中的方法带入 A 的范围:

struct B{
   void method1(){};
   void method2(){};
};
struct A : private B{
   using B::method1;
};

A a;
a.method1();
a.method2(); //error method2 is not accesible

【讨论】:

    【解决方案2】:

    这里有一个问题:这将直接违反 Liskov 替换原则,即 A 将不再充当 B

    如果您希望重用B 实现,解决方案就是这样做:

    class A
    {
    public:
      void foo() { return b.foo(); }
      void bar() { return b.bar(); }
      // ...
    
    private:
      B b;
    };
    

    不要滥用继承,而是使用组合

    【讨论】:

    • 只是碰巧必须写一个类,它是另一个类的 99%,但处理方式不同并且有 1 个函数,只有一个 B 中不存在的函数。
    • 不一定是滥用继承,只是说明B不是A的子类型(如公有继承),但B是按照A来实现的,所以LSP不受影响。我相信“使用组合代替”通常是一个很好的建议,但非公共继承可能是实现 Facade 模式的有用工具,即看看 javax.swing.JButton 是多么臃肿。
    • @JanTuroň:请注意,私有继承意味着 LSP 适用于所有访问类内部的人:类本身、它的子类和它的朋友。那些可能会不小心调用基础上的方法,或者将指向基础的引用/指针传递给不相关的函数。通过更加深思熟虑,构图有助于更好地抵御墨菲。
    • 那当然是设计问题。如果继承是非公开的,我认为如果有人试图从 B 向下转换为 A,编译器至少应该发出警告。请参阅我应该将指针从私有派生类转换为它的基类吗? here.
    【解决方案3】:

    using 关键字可用于更改可见性

    struct A
    {
        void method1();
    };
    
    struct B: public A
    {
        void method2();
    
        private:
        using A::method1;
    };
    

    【讨论】:

      【解决方案4】:

      除了前面的答案中描述的方式——组合、私有继承和非私有继承,但继承方法声明为私有——另一种方法是显式delete继承方法:

      #include <iostream>
      
      struct A {
          void foo() { std::cout << "foo\n"; }
      };
      
      struct B : A {
          void foo() = delete;
      };
      
      int main() {
          B b;
          b.foo(); // COMPILER ERROR
      }
      

      虽然b.foo() 调用会产生编译器错误,但客户端代码仍然可以通过使用基类标识符A 限定来调用基类的版本:

      b.A::foo(); // compiles, outputs 'foo' to console
      

      foo 不是A 中的虚拟未删除方法时,这种显式删除方式有效。根据 C++11 标准 §10.3/16,当派生类中的已删除方法覆盖基类的虚拟未删除方法时,此显式删除是错误的。有关此限制的更多信息,请参阅 SO 问题 C++11 Delete Overriden Method 的答案。

      【讨论】:

      • 这对于选择性隐藏方法非常有用。非常感谢!
      【解决方案5】:

      你不能“隐藏”它本身,但你可以让它成为编译时错误来调用它。示例:

      struct A
      {
          void AMethod() {}
      };
      
      class B : public A
      {
          void AMethod() {} //Hides A::AMethod
      };
      
      int main()
      {
          B myB;
          myB.AMethod(); //Error: AMethod is private
          static_cast<A*>(&myB)->AMethod(); //Ok
          return 0;
      }
      

      键盘with the errorwithout 上的示例。

      话虽如此,尽管这是可能的,但你真的不应该这样做。你会把客户搞糊涂的。

      编辑:请注意,您也可以使用 virtual functions(和 with the error)来执行此操作。

      【讨论】:

      • 我认为没有必要在继承类 B 中定义(私有)AMethod。我认为声明就足够了。我会说原始方法是“隐藏的”,如果尝试使用它是编译时错误(即使错误消息与该方法根本不存在的情况不同)。
      • @eq:没错,除非父类中的方法是virtual,否则不需要定义它。 (我只是懒了,用复制/粘贴来制作B
      【解决方案6】:

      对于那些建议组合的人......这可能不是最好的处理方式。我的理解是,Liskov 替换原则仅表明基类中的函数有可能用于子类,而不是它们必须如此。例如,对于特定的基类,您可能有多个函数,它们基本上执行相同的操作,但针对不同的特定情况。在派生类中,您可能希望将这些公共函数抽象出来,以简化用户界面。这是可以使用私有继承的地方。私有继承也可能是必需的,如果我们在基类中有受保护的函数,我们不希望基类的用户调用,但对派生类来说是无价的。

      简而言之,如果必须,请使用私有继承,但在大多数情况下首选组合。

      【讨论】:

        【解决方案7】:

        还有另一种方法。

        class A{
            void f1();
            void f2();
            void f3();
        }
        
        class BInterface{
            void f2();
            void f3();
        }
        
        class B : public A, BInterface
        {
        }
        
        BInterface b = new B();
        b->f1(); //doesn't work since f1 is not declared in BInterface
        b->f2(); //should work
        b->f3(); //should work
        delete(b);
        

        使用 BInterface 作为继承类的过滤器以排除不需要的方法。在这种情况下不违反里氏替换原则,因为 BInterface 类的对象不是 A 类的对象,即使 B 类的对象是 BInterface 类的对象。

        【讨论】:

          【解决方案8】:

          如果方法在 B 中是私有的,那么即使您使用公共继承,它们也会对 a 保持隐藏。

          【讨论】:

            【解决方案9】:

            无法更改原始方法的可见性。

            您可以在 struct A 中创建一个具有相同名称的方法并将该方法设为私有方法,但这并不妨碍当 struct A 的实例被以下变量引用时调用该方法输入B

            【讨论】:

              【解决方案10】:

              为什么不在基类中将其设为 Virtual 并在其子类中覆盖它? (more help)

              【讨论】:

                猜你喜欢
                • 2014-05-24
                • 2011-05-23
                • 2020-01-23
                • 2013-02-25
                • 1970-01-01
                • 1970-01-01
                • 2020-08-24
                • 2011-11-02
                • 2021-02-14
                相关资源
                最近更新 更多