代码之家  ›  专栏  ›  技术社区  ›  Gohan

继承共享方法名的接口

  •  51
  • Gohan  · 技术社区  · 15 年前

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

    #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中的定义去掉。我发现微软特定的关键字uuu接口可以成功完成这项工作,代码如下:

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

    但是,有没有其他方法可以在其他编译器中使用更通用的方法呢?

    5 回复  |  直到 8 年前
        1
  •  63
  •   Max Lybbert    8 年前

    这个问题不常出现。我熟悉的解决方案是由DougMcIlroy设计的,并出现在Bjarne Stroustrup的书中(两本书都有介绍 C++的设计与演化 第12.8条和 C++程序设计语言 第25.6条)。根据中的讨论 设计与发展 有人提议优雅地处理这一具体案件,但被拒绝了,因为“这样的名字冲突不太可能变得足够普遍,不足以保证独立的语言功能”,“不太可能成为新手的日常工作”。

    你不仅需要打电话 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();
    }
    

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

        2
  •  6
  •   Len Holgate    13 年前

    在过去,我必须这样做,但是在我的例子中,我需要从一个接口继承两次,并且能够区分对每个接口所做的调用,我使用模板填充程序来帮助我……

    像这样:

    template<class id>
    class InterfaceHelper : public MyInterface
    {
        public : 
    
           virtual void Name() 
           {
              Name(id);
           }
    
           virtual void Name(
              const size_t id) = 0;  
    }
    

    然后你就从 InterfaceHelper 两次而不是从 MyInterface 两次,然后指定不同的 id 对于每个基类。然后,您可以通过强制转换到正确的 幕间帮手 .

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

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

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

        3
  •  5
  •   Community davidgyoung    7 年前

    不能单独覆盖它们,必须同时覆盖这两个:

    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上调用名称(使用虚拟调度,例如 rc.Name() )必须首先选择一个基。self macro是清理crtp强制转换的一种简单方法(通常成员访问在crtp库中更常见),但它可以 improved . 在我的一个 other answers 但如果有人有联系,肯定会是更好的。

        4
  •  3
  •   Jagannath    15 年前
    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
    {
    
    };
    
        5
  •  1
  •   Community davidgyoung    7 年前

    还有两个相关问题几乎(但不是完全)相同:

    Picking from inherited shared method names . 如果要让rc.name()调用ic1->name()。 IC2->名称()。

    Overriding shared method names from (templated) base classes .它的语法和代码比您接受的解决方案简单,但是 没有 允许从派生类访问函数。或多或少,除非您需要能够从RC调用name_i1(),否则不需要使用InterfaceHelper之类的东西。