gcc 如何重写两个基类中同名的虚成员函数

tsm1rwdh  于 2023-10-19  发布在  其他
关注(0)|答案(5)|浏览(107)

有两个基类具有相同的函数名。我想继承这两个方法,并以不同的方式重写每个方法。如何使用单独的声明和定义(而不是在类定义中定义)来实现这一点?

#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();
}

我在VC8中没有把定义移出来。我发现Microsoft Specific Keyword __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();
}

但是有没有其他的方法可以做到这一点,一些更通用的方法可以在其他编译器中工作?

rryofs0p

rryofs0p1#

这个问题不常出现。我熟悉的解决方案是由Doug McIlroy设计的,出现在Bjarne Stroustrup的书中(在 Design & Evolution of C++ 第12.8节和 The C++ Programming Language 第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();
}

不太好,但决定是不经常需要。

zmeyuzjn

zmeyuzjn2#

您不能单独覆盖它们,必须同时覆盖两者:

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");
}

您可以使用中间基类模拟单独的重写:

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
  }
};

另外,你错误地使用了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;
}

这里有一个用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") {}
};

但是请注意,这里您现在不能在RealClass上调用Name(使用虚拟分派,例如rc.Name()),则必须先选择一个基础。self宏是清理CRTP强制转换的一种简单方法(通常成员访问在CRTP库中更为常见),但它可以是improved。在我的另一个答案中有一个关于虚拟调度的简短讨论,但如果有人有链接,肯定会有一个更好的答案。

qmelpv7a

qmelpv7a3#

过去我不得不做这样的事情,尽管在我的情况下,我需要从一个接口继承两次,并且能够区分对每个接口的调用,我使用了模板shim来帮助我。
大概是这样的:

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

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

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

然后从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?");
          }
      }  
}

请注意,上面还没有看到编译器...

1qczuiv0

1qczuiv04#

class BaseX
{
public:
    virtual void fun()
    {
        cout << "BaseX::fun\n";
    }
};

class BaseY
{
public:
    virtual void fun()
    {
        cout << "BaseY::fun\n";
    }
};

class DerivedX : protected BaseX
{
public:
    virtual void funX()
    {
        BaseX::fun();
    }
};

class DerivedY : protected BaseY
{
public:
    virtual void funY()
    {
        BaseY::fun();
    }
};

class DerivedXY : public DerivedX, public DerivedY
{

};
xv8emn3q

xv8emn3q5#

还有另外两个相关的问题,问的是几乎(但不完全)相同的问题:
从继承的共享方法名称中选取。如果你想让rc.name()调用ic 1->name()ic 2->name()。
Overriding shared method names from (templated) base classes。这比你接受的解决方案有更简单的语法和更少的代码,但是 * 不 * 允许从派生类访问函数。或多或少,除非您需要能够从rc调用name_i1(),否则您不需要使用InterfaceHelper之类的东西。

相关问题