继承共享方法名称的接口

Goh*_*han 56 c++ gcc multiple-inheritance visual-c++

有两个基类具有相同的函数名.我想继承它们,并以不同的方式搭乘每种方法.如何使用单独的声明和定义(而不是在类定义中定义)?

#include <cstdio>

class Interface1{
public:
    virtual void Name() = 0;
};

class Interface2
{
public:
    virtual void Name() = 0;
};

class RealClass: public Interface1, public Interface2
{
public:
    virtual void Interface1::Name()
    {
        printf("Interface1 OK?\n");
    }
    virtual void Interface2::Name()
    {
        printf("Interface2 OK?\n");
    }
};

int main()
{
    Interface1 *p = new RealClass();
    p->Name();
    Interface2 *q = reinterpret_cast<RealClass*>(p);
    q->Name();
}   
Run Code Online (Sandbox Code Playgroud)

我没能在VC8中移出定义.我发现Microsoft特定关键字__interface可以成功完成这项工作,代码如下:

#include <cstdio>

__interface Interface1{
    virtual void Name() = 0;
};

__interface Interface2
{
    virtual void Name() = 0;
};

class RealClass: public Interface1,
                public Interface2
{
public:
    virtual void Interface1::Name();
    virtual void Interface2::Name();
};

void RealClass::Interface1::Name()
{
    printf("Interface1 OK?\n");
}

void RealClass::Interface2::Name()
{
    printf("Interface2 OK?\n");
}

int main()
{
    Interface1 *p = new RealClass();
    p->Name();
    Interface2 *q = reinterpret_cast<RealClass*>(p);
    q->Name();
}  
Run Code Online (Sandbox Code Playgroud)

但还有另一种方法可以在其他编译器中使用更通用的东西吗?

Max*_*ert 68

这个问题不常出现.我熟悉的解决方案是由Doug McIlroy设计并出现在Bjarne Stroustrup的书中(在C++的设计和演变部分12.8和C++编程语言部分25.6中介绍).根据" 设计与进化"中的讨论,有一个提议优雅地处理这个特定情况,但它被拒绝了,因为"这样的名称冲突不太可能变得普遍,不足以保证单独的语言功能",并且"不太可能成为每天为新手工作."

你不仅需要调用Name()通过指针到基类,你需要一个方式来表达 Name()想要在派生类中操作时.该解决方案增加了一些间接性:

class Interface1{
public:
    virtual void Name() = 0;
};

class Interface2{
public:
    virtual void Name() = 0;
};

class Interface1_helper : public Interface1{
public:
    virtual void I1_Name() = 0;
    void Name() override
    {
        I1_Name();
    }
};

class Interface2_helper : public Interface2{
public:
    virtual void I2_Name() = 0;
    void Name() override
    {
        I2_Name();
    }
};

class RealClass: public Interface1_helper, public Interface2_helper{
public:
    void I1_Name() override
    {
        printf("Interface1 OK?\n");
    }
    void I2_Name() override
    {
        printf("Interface2 OK?\n");
    }
};

int main()
{
    RealClass rc;
    Interface1* i1 = &rc;
    Interface2* i2 = &rc;
    i1->Name();
    i2->Name();
    rc.I1_Name();
    rc.I2_Name();
}
Run Code Online (Sandbox Code Playgroud)

不漂亮,但决定是不经常需要它.

  • 你的答案很简单,很有帮助,谢谢〜 (3认同)
  • 您提供了提案的来源真是太棒了,虽然不太确定为什么您不将提案本身的内容包含在实际答案中,至少作为一个“有趣的事实”,无论多么简短。为了填满自己,我不得不在网上寻找这本书。相当有趣的阅读。对于未来的读者:被拒绝的提案引入了以下语法来“重命名”已实现接口的方法:`virtual void Name1() = Interface1::Name;`干杯 (3认同)
  • 你很快就会思考和输入; v).+1 (2认同)

小智 6

您不能单独覆盖它们,您必须一次覆盖它们:

struct Interface1 {
  virtual void Name() = 0;
};

struct Interface2 {
  virtual void Name() = 0;
};

struct RealClass : Interface1, Interface2 {
  virtual void Name();
};
// and move it out of the class definition just like any other method:
void RealClass::Name() {
  printf("Interface1 OK?\n");
  printf("Interface2 OK?\n");
}
Run Code Online (Sandbox Code Playgroud)

您可以使用中间基类模拟单个覆盖:

struct RealClass1 : Interface1 {
  virtual void Name() {
    printf("Interface1 OK?\n");
  }
};

struct RealClass2 : Interface2 {
  virtual void Name() {
    printf("Interface2 OK?\n");
  }
};

struct RealClass : RealClass1, RealClass2 {
  virtual void Name() {
    // you must still decide what to do here, which is likely calling both:
    RealClass1::Name();
    RealClass2::Name();

    // or doing something else entirely

    // but note: this is the function which will be called in all cases
    // of *virtual dispatch* (for instances of this class), as it is the
    // final overrider, the above separate definition is merely
    // code-organization convenience
  }
};
Run Code Online (Sandbox Code Playgroud)

此外,您正在使用reinterpret_cast错误,您应该:

int main() {
  RealClass rc; // no need for dynamic allocation in this example

  Interface1& one = rc;
  one.Name();

  Interface2& two = dynamic_cast<Interface2&>(one);
  two.Name();

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这里是CRTP的重写,可能是你想要的(或不是):

template<class Derived>
struct RealClass1 : Interface1 {
#define self (*static_cast<Derived*>(this))
  virtual void Name() {
    printf("Interface1 for %s\n", self.name.c_str());
  }
#undef self
};

template<class Derived>
struct RealClass2 : Interface2 {
#define self (*static_cast<Derived*>(this))
  virtual void Name() {
    printf("Interface2 for %s\n", self.name.c_str());
  }
#undef self
};

struct RealClass : RealClass1<RealClass>, RealClass2<RealClass> {
  std::string name;
  RealClass() : name("real code would have members you need to access") {}
};
Run Code Online (Sandbox Code Playgroud)

但请注意,现在您无法在RealClass上调用Name(例如rc.Name(),使用虚拟调度),您必须首先选择一个基数.自我宏是一种简单的方法来清理CRTP转换(通常成员访问在CRTP基础中更常见),但它可以改进.在我的其他一个答案中有一个关于虚拟调度的简短讨论,但如果有人有链接肯定会更好.


Len*_*ate 6

我不得不做这样的事情在过去,虽然在我的情况,我需要从一个接口继承两次,并能够在他们每个人的呼叫进行区分,我用一个模板垫片帮我...

像这样的东西:

template<class id>
class InterfaceHelper : public MyInterface
{
    public : 

       virtual void Name() 
       {
          Name(id);
       }

       virtual void Name(
          const size_t id) = 0;  
}
Run Code Online (Sandbox Code Playgroud)

然后,您从InterfaceHelper两次而不是MyInterface两次派生,并id为每个基类指定一个不同的.然后,您可以通过转换为正确的方式独立地分发两个接口InterfaceHelper.

你可以做一些稍微复杂的事情;

class InterfaceHelperBase
{
    public : 

       virtual void Name(
          const size_t id) = 0;  
}


class InterfaceHelper1 : public MyInterface, protected InterfaceHelperBase
{
    public : 

       using InterfaceHelperBase::Name;

       virtual void Name() 
       {
          Name(1);
       }
}

class InterfaceHelper2 : public MyInterface, protected InterfaceHelperBase
{
    public : 

       using InterfaceHelperBase::Name;

       virtual void Name() 
       {
          Name(2);
       }
}

class MyClass : public InterfaceHelper1, public InterfaceHelper2
{
    public :

      virtual void Name(
          const size_t id)
      {
          if (id == 1) 
          {
              printf("Interface 1 OK?");
          }
          else if (id == 2) 
          {
              printf("Interface 2 OK?");
          }
      }  
}
Run Code Online (Sandbox Code Playgroud)

注意上面没有见过编译器......