【问题标题】:Overloaded method overriding: Is there any simplification?重载方法覆盖:有什么简化吗?
【发布时间】:2013-11-18 08:47:26
【问题描述】:

我正在编写一个使用讨厌的非 C++11 代码的类的覆盖。不幸的是,我不允许更改父类。 它有两个同名的方法,其中一个被子类覆盖。该方法也作为参数传递给方法。我想在这里有函子,但正如我之前写的,我无法更改 CLassA。 简化后的代码如下所示:

父类:

class CLassA
{
public:
    typedef void  (CLassA::*Method_t)(int x, int y);
    int ForAll(Method_t action, int y);
    virtual void MethodA(int x);
    virtual void MethodA(int x, int y);
};

int CLassA::ForAll(Method_t action, int x)
{
    (this->*action)(x, x);
    return x;
}

void CLassA::MethodA(int x)
{
    printf("CLassA::MethodA: %d\n", x);
    ForAll(&CLassA::MethodA, x);
}

void CLassA::MethodA(int x, int y)
{
    printf("\tCLassA::MethodA: %d %d\n", x, y);
}

我的第一个覆盖:

class CLassB : public CLassA
{
public:
    void MethodA(int x, int y) override;
};

void CLassB::MethodA(int x, int y)
{
    printf("\tCLassB::MethodA: %d %d\n", x, y);
}

我的第二次覆盖:

class CLassC : public CLassB
{
public:
    void MethodA(int x) override;
};

void CLassC::MethodA(int x)
{
    printf("CLassC::MethodA: %d\n", x);
    ForAll(&CLassC::CLassA::MethodA, x);
}

调用例程:

int main(int argc, char** argv)
{
    CLassA A;
    A.MethodA(1);

    CLassB B;
    B.CLassA::MethodA(2);

    CLassC C;
    C.MethodA(3);
    return 0;
}

输出(link):

CLassA::MethodA: 1 CLassA::MethodA: 1 1 CLassA::MethodA: 2 CLassB::MethodA: 2 2 CLassC::MethodA: 3 CLassB::MethodA: 3 3

我绝对不喜欢,我必须将命名空间添加到引用和调用中(ForAll(&CLassC::CLassA::MethodA, x);B.CLassA::MethodA(2);)。

您能否给我一个提示,如何避免这些丑陋的符号?

【问题讨论】:

  • 我可能弄错了,但我不认为CLassC::CLassA::MethodA 中的CLassC:: 实际上是必需的。 CLassA 似乎是 CLassC 中的明确标识符。
  • 另外,应该提到的是,因为CLassBMethodA 的定义是非虚拟的,如果你有一个CLassC,当前被标识为CLassB 或@987654336 @(例如在CLassB 或类似的向量中),它将调用MethodACLassB 版本而不是CLassC 版本。我不确定这是不是有意的。
  • 你能告诉我们更多关于你真正想要做什么的细节吗?例如,如果你打电话给B.CLassA::MethodA(2),你还会打电话给B.MethodA吗?如果不是,为什么要让CLassB 覆盖MethodA,如果是,为什么不直接创建一个MethodB 为你调用CLassA::MethodA
  • @OmnipotentEntity ClassB::MethodA 是隐式虚拟的(否则 override 说明符会导致错误),我添加了显示它的输出。顺便说一句,使用警告选项-Woverloaded-virtual GCC 会警告某些虚函数被覆盖隐藏

标签: c++ c++11 overriding


【解决方案1】:

当您在派生类中声明函数时,基类中的任何同名重载都会被隐藏。无论函数是重载还是覆盖,此规则都适用。当重载virtual 函数时,尤其是当它们有实现时,通常的方法是拥有public 和非virtual 转发函数,它们调用protected 函数。这样virtual 函数就可以被单独覆盖而不影响调用接口。参见例如std::num_get<...>

当您在基类中重载virtual 函数时,您无法更改最好的方法是使用using 声明使基类重载可见:

class CLassB: public CLassA {
public:
    using CLassA::MethodA;
    void MethodA(int, int) override;
};

【讨论】:

    【解决方案2】:

    当您只覆盖其中一个潜在的重载方法时,请添加“using BaseClass::MethodA;”到你的孩子班。

    所以,ClassB 声明变成了;

    class ClassB : public ClassA {
     public:
      using ClassA::MethodA;
      virtual void MethodA(int x, int y) override;
    };
    

    你的定义不会改变。然后你可以做B.MethodA(2);,而不需要插入中间的ClassA来消除歧义。

    【讨论】:

      【解决方案3】:

      当您使用相同名称覆盖两个方法重载之一时,会出现某种名称冲突。将第二种方法重命名为 MethodB 应该可以解决您的 B.ClassA::MethodA(2)problem:

      class CLassA
      {
      public:
          typedef void  (CLassA::*Method_t)(int x, int y);
          int ForAll(Method_t action, int y);
          virtual void MethodA(int x);
          virtual void MethodB(int x, int y);
      };
      

      对于ForAll(&CLassC::CLassA::MethodA, x);,这只是类型推断的问题。 要么你应该施放它:

      void CLassC::MethodA(int x)
      {
          printf("CLassC::MethodA: %d\n", x);
          ForAll((Method_t)&CLassC::MethodB, x);
      }
      

      或者也许使用 std::function 而不是原始方法指针应该可以解决这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-04
        • 2010-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-11
        相关资源
        最近更新 更多