Hay dos clases de base tienen el mismo nombre de la función. Quiero heredar tanto de ellos, y más de paseo cada método diferente. Cómo puedo hacer que con la declaración por separado y la definición (en lugar de definir en la definición de la clase)?

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

Yo no se pudo mover la definición en VC8. He encontrado el de Microsoft palabra Clave Específica __interfaz puede hacer este trabajo con éxito, el siguiente código:

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

pero hay otra manera de hacer esto algo más general, que funcionará en otros compiladores?

  • ¿Por qué quieres esto? Parece que está derrotando el propósito de funciones virtuales; simplemente replicar la no-virtual semántica. ¿Usted tiene un de referencia para __interface? Lo he buscado y no parece hacer nada esencial.
  • He añadido el __modo de interfaz.
  • Potatoswatter – la razón por la que usted puede ser que necesite para hacer esto es que usted puede tener una clase que reúne dos conceptos diferentes por heredar de dos interfaces. Los usuarios de la clase de uso probable que a través de las interfaces y de los usuarios de las dos interfaces diferentes no saben o se preocupan el uno al otro. La clase trae estos juntos y necesidades para diferenciar entre las llamadas en una sola interfaz, y pide a los otros. De ninguna manera se te derrotando el propósito de las funciones virtuales. Gohan – me sorprendería si __inteface de hecho una diferencia aquí.
  • Es diferente, Si desea mover el poner en práctica parte directamente, el __de la interfaz de palabra clave va a hacer que funcione, y usando el estándar de la clase o struct sólo contienen las funciones virtuales puras se obtuvo el error de los compiladores.
  • simplemente replicar la no-virtual semántica.» ¿cómo es exactamente que «replicantes no virtual semántica»?
  • Así, es Interface1::Name un unnombre completo?

InformationsquelleAutor Gohan | 2010-01-05

5 Comentarios

  1. 67

    Este problema no viene muy a menudo. La solución que estoy familiarizado fue diseñado por Doug McIlroy y aparece en Bjarne Stroustrup libros (que se presenta en ambos Diseño & Evolución de C++ sección 12.8 y El Lenguaje de Programación C++ sección 25.6). De acuerdo a la discusión en Diseño & Evolución, hay una propuesta para manejar este caso específico elegante, pero fue rechazado porque «nombre de enfrentamientos fueron poco probable llegar a ser lo suficientemente comunes como para justificar una lengua distinta función,» y «no es probable para convertirse en el trabajo diario para los novatos.»

    No sólo debe llamar a Name() a través de punteros a clases base, usted necesita una manera de decir que Name() desea cuando se opera en la clase derivada. La solución que agrega un poco de direccionamiento indirecto:

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

    No es bonita, pero la decisión fue no es necesario a menudo.

    • Su respuesta es simple y bastante útil, muchas gracias~
    • Que pensar y escribir muy rápidamente ;v) . +1
    • Es maravilloso que siempre las fuentes de la propuesta, no muy seguro de si ¿por qué no incluir el contenido de la propuesta en la respuesta real, como «un dato interesante» al menos, aunque sea brevemente. Con el fin de llenar de mí mismo en la que tuve que buscar el libro en línea. Muy interesante de leer. Para los lectores futuros: rechazaron la propuesta introduce la siguiente sintaxis para «cambiar el nombre de» un método de una interfaz implementada: virtual void Name1() = Interface1::Name; Saludos
  2. 6

    No se puede reemplazar por separado, debe reemplazar los dos a la vez:

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

    Puede simular individuales primordial con el intermedio de clases base:

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

    Además, el uso de reinterpret_cast incorrectamente, usted debe tener:

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

    Y he aquí una reescritura con CRTP que podría ser lo que usted desea (o no):

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

    Pero tenga en cuenta que aquí no se puede llamar a Nombre de un RealClass ahora (con virtual despacho, por ejemplo,rc.Name()), primero se debe seleccionar una base. El auto macro es una manera fácil de limpiar CRTP moldes (generalmente de acceso de los miembros es mucho más común en la CRTP base), pero puede ser mejora de la. Hay una breve discusión de virtual despacho en uno de mis otras respuestas, pero sin duda uno mejor de todo si alguien tiene un enlace.

    • Su intermedio clases base no parecen hacer nada. Usted puede llamar a Interface1::Name de la misma manera como RealClass1::Name.
    • … Quiero decir, podría, si se implementa como para RealClass1.
    • Usted no puede llamar a rc.Interface1::Name(), que es una función virtual pura sin aplicación.
    • También tenga en cuenta todo este código es el estándar de C++ y debería funcionar con cualquier compilador de C++, aunque yo en realidad sólo probado la CRTP caso, y simplemente se demostró que el resto correcto.
    • Cubierto que, en mi segundo comentario ;v) . Si se puede agregar la clase intermedia, probablemente se puede agregar la aplicación a la clase de la que ya tiene. De todos modos, sólo se derrota a la vtable de búsqueda.
    • No lo quiero, pero el virtual envío parte es genial, gracias
    • Potatoswatter: no entiendo lo que estás diciendo. Estoy asumiendo que él no puede cambiar la interfaz de la base, por lo que «si se implementa como para RealClass1» no es posible. La clase intermedia (que otras respuestas utilizar y Gohan dijo que él encontró útil) permite disponer de final por separado overriders para Interfaz1 y Interfaz2 (no hay otra manera de lograr que), y permite una forma alternativa para especificar la base (rc.RealClass1::Name(), pero con un nombre mejor, vs ((Interface1&)rc).Name()).
    • Gohan: lo Siento, no podía presentar mejor, pero estoy contento de que algunos de los que fue útil.
    • Primordial de ambos a la vez, es a menudo suficiente.

  3. 6

    He tenido que hacer algo como esto en el pasado, aunque en mi caso necesitaba para heredar de una interfaz de dos veces y ser capaz de diferenciar entre las llamadas realizadas en cada uno de ellos, he utilizado una plantilla de cuña para que me ayude…

    Algo como esto:

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

    Que luego se derivan de InterfaceHelper dos veces en lugar de a partir de MyInterface dos veces y se especifique otra id para cada clase base. Usted puede, a continuación, de la mano de dos interfaces de forma independiente por el casting para la correcta InterfaceHelper.

    Se podría hacer algo un poco más complejo;

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

    Tenga en cuenta que la anterior no ha visto un compilador…

    • No hay necesidad de utilizar un compilador para ver que Name(2); no puede ser un llamada recursiva, ya que esta función Name no toma ningún argumento!
    • No pretende ser una llamada recursiva. El real implementaciones de Nombre() llamar a una versión que incluye un Id de modo que usted puede saber que versión de (Nombre) fue llamado en el objeto base. Usted, a continuación, pasar los objetos de ayuda a quien sucede a la necesidad real de la interfaz.
    • Pero como está escrito sólo puede ser una llamada recursiva. Usted puede utilizar el operador de resolución de ámbito para llamar a otro Name función, por ejemplo.
    • ahora se ha visto un compilador, aunque sea brevemente…
  4. 3
    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

    Hay otros dos relacionados con las preguntas que piden casi (pero no completamente) cosas idénticas:

    Recolección de las heredado método compartido nombres. Si usted quiere tener la rc.nombre() la llamada ic1->nombre() o ic2->nombre de().

    Primordial método compartido nombres de (plantilla) de clases base. Esto ha sintaxis más simple y menos código que su solución, pero no permitir el acceso a las funciones de la clase derivada. Más o menos, a menos que usted necesita para ser capaz de llamar a name_i1() de un rc, usted no necesita usar cosas como InterfaceHelper.

Dejar respuesta

Please enter your comment!
Please enter your name here