【问题标题】:C++ multiple inheritance - same method names - can I somehow remove one of them?C++ 多重继承 - 相同的方法名称 - 我可以以某种方式删除其中一个吗?
【发布时间】:2017-08-01 21:22:22
【问题描述】:

我在 C++11 中有这个结构

struct A {
   int idA;   
   void setId(int i) { idA = i;}
   int getId() { return idA;}

   virtual void foo() = 0;
};

struct B {
   int idB;   
   void setId(int i) { idB = i;}
   int getId() { return idB;}

   virtual void foo2() = 0;
};

struct AB : public A, public B
{
   void foo() override {}
   void foo2() override {}
};

现在在 main 我可以这样称呼它:

AB * ab = new AB();
ab->B::setId(10);

但我真的不喜欢它。

还有其他解决方案吗? AB 中不需要来自 struct A 的方法 setId/getId。我只是做了继承,因为我需要foo()A 的其他东西,但所有其他方法都是独一无二的。

【问题讨论】:

  • 你能修改你的层次结构吗?例如。更改A 并引入额外的类
  • @VittorioRomeo 我可以(以有限的方式)
  • 当你说你“需要”foo() 和来自A 的其他东西时,你指的是声明还是定义?
  • 可以把A类分解成别的东西。

标签: c++ c++11 inheritance


【解决方案1】:

既然你说你不需要A的那些方法版本,你可以写

struct AB : public A, public B
{
    void foo() override {}
    void foo2() override {}

    using B::setId;
    using B::getId;
};

这会将B 的这些方法的实现放入AB 的命名空间中,并明确调用它们。

【讨论】:

  • 这种方法的一个问题可能是A::setId 在这种情况下仍然会被调用:A* ab = new AB(); ab->setId(0);
【解决方案2】:

包装器转发方法呢:

struct AB : public A, public B
{
public:
    void setAId(int i) { A::setID(i); }
    void setBId(int i) { B::setID(i); }
};

这样您就不会成为名称隐藏的“受害者”,您的意图在代码中变得清晰,并且您的名称反映了他们所做的事情,并且您不需要访问基类的成员(es ) 明确地。

或者,您可以创建另一个基类并在AB 中虚拟继承它,其中您将包含setId 方法。

【讨论】:

  • 这会起作用,但有人仍然可以调用 setId() 并得到错误。
  • @MartinPerry 那么它应该在基类imo中受到保护。
  • @Ressurection 这将是不正确的,因为其他类仅从 A 或仅从 B 继承,并且允许它们调用这些方法
  • @MartinPerry 如果您在继承中有不想要的公共方法,那么您应该将基类组合或拆分为您实际想要继承的部分和其余部分。这样,两种情况都会起作用。名称隐藏真的不应该用于此。
【解决方案3】:

我相信我的答案和公认的答案都会带来相当烦人的问题,请考虑:

AB ab;
A *a = &ab;
a->setId(10); //problem
a->foo(); //calls AB::foo() correctly

当使用名称隐藏(接受的答案)时,AB 对象永远不会被调用,而我的答案(包装调用)也没有考虑到这一点。我认为在这种情况下正确的方法是使用Aprivate inehritance 并仅公开foo(),因此只有一件事会与原始OP的代码发生变化:

struct AB : private A, public B

【讨论】:

    【解决方案4】:

    只要您在 AB 类型的对象或 AB 指针上调用 setId(int),接受的答案就可以工作(大多数情况下,当然可以)。但是如果你想调用A* ab = new AB(); 上的函数,你必须明确地覆盖它。

    struct A {
       int idA;   
       virtual void setId(int i) { idA = i;}
    };
    
    struct B {
       int idB;   
       virtual void setId(int i) { idB = i;}
    };
    
    struct AB : public A, public B
    {
        void setId(int i) override { B::setId(i); }
    };
    

    【讨论】:

      【解决方案5】:

      通常使用 OO 和继承,如果当时不知道功能重用,通常最好多次实现该功能,直到确定可以将其分解的模式。

      例如,将类对象作为数据容器并从中继承并没有错。

      class IdentificationData {
          int ID;
          . . .
      }
      

      您也不一定需要让函数成为类的一部分。类只是一个容器,用于组织和封装数据、可能对数据进行操作的函数或函数集合到一个公共集合中。

      class IdentificationFunctionality{
           static SetID( IdentificationData* obj) {...}
      }
      

      这样做可以让你做类似...

      class AB: IdentificationData, B {
          . . .
      }
      
      AB* ab = new AB();
      IdentificationFunctionality::SetID(&ab);
      

      这样做的另一个好处是,这将允许您进行一些簿记,而您的对象不需要知道数据库系统的详细信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 1970-01-01
        • 2023-03-18
        • 2012-06-14
        • 2011-10-09
        • 2015-07-05
        • 1970-01-01
        相关资源
        最近更新 更多