Унаследуйте интерфейсы, которые разделяют название метода

Есть два базовых класса, имеют то же имя функции. Я хочу унаследовать их обоих, и по поездке каждый метод по-другому. Как я могу сделать это с отдельной декларацией и определением (вместо того, чтобы определить в определении класса)?

#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 __, интерфейс может сделать эту работу успешно, кодекс ниже:

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

но есть ли другой способ сделать это что-то более общее, которое будет работать в других компиляторах?

56
задан Gohan 5 January 2010 в 09:28
поделиться

5 ответов

Эта проблема возникает нечасто. Решение, с которым я знаком, было разработано Дагом Макилроем и изложено в книгах Бьярна Стоуструпа (представленных в разделах 12.8 Дизайн и эволюция С++ и 25.6 Язык программирования С++). Согласно дискуссии в Design & Evolution, предлагалось элегантно подойти к этому конкретному случаю, но оно было отвергнуто, так как "такие столкновения названий вряд ли станут достаточно распространенными, чтобы оправдать выделение отдельного языка", и "вряд ли станут повседневной работой для новичков". "

Нужно не только вызывать 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();
}

Не красиво, но решение было принято не часто.

.
68
ответ дан 26 November 2019 в 17:26
поделиться

Раньше мне приходилось делать что-то подобное, хотя в моем случае мне нужно было дважды унаследовать от одного интерфейса и иметь возможность различать вызовы, сделанные на каждом из них, я использовал шаблонный шимпель, чтобы помочь мне. ...

Что-то вроде этого:

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

Обратите внимание, что вышеприведенный компилятор...

не встречался.
6
ответ дан 26 November 2019 в 17:26
поделиться

Еще два связанных вопроса задают почти (но не полностью) одинаковые вещи:

Выбор из унаследованных общих имен методов. Если Вы хотите, чтобы функция rc.name() вызывала ic1->name() или ic2->name().

Переопределение имен общих методов из (шаблонированных) базовых классов. Это имеет более простой синтаксис и меньшее количество кода, чем принятое вами решение, но не позволяет доступ к функциям из производного класса. Более или менее, если только вам не нужно иметь возможность вызывать name_i1() из rc, вам не нужно использовать такие вещи, как InterfaceHelper.

1
ответ дан 26 November 2019 в 17:26
поделиться

Вы не можете переопределить их по отдельности, вы должны переопределить оба сразу:

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

Но обратите внимание, что здесь вы не можете сейчас вызывать Name на RealClass (с виртуальной диспетчеризацией, e. g. rc.Name()), сначала необходимо выбрать базу. Сам макрос является простым способом очистки кастов CRTP (обычно доступ членов гораздо более распространен в базе CRTP), но он может быть улучшен. В одном из моих ответов кратко обсуждается виртуальная отправка, но, безусловно, лучше, если у кого-то есть ссылка.

.
6
ответ дан 26 November 2019 в 17:26
поделиться
[
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
{

};
]
3
ответ дан 26 November 2019 в 17:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: