7

hice un código de prueba de la siguiente manera:ambigüedad en la herencia múltiple de interfaces en C++

#include <iostream> 
using namespace std; 

#ifndef interface 
#define interface struct 
#endif 

interface Base 
{ 
    virtual void funcBase() = 0; 
}; 

interface Derived1 : public Base 
{ 
    virtual void funcDerived1() = 0; 
}; 

interface Derived2 : public Base 
{ 
    virtual void funcDerived2() = 0; 
}; 

interface DDerived : public Derived1, public Derived2 
{ 
    virtual void funcDDerived() = 0; 
}; 

class Implementation : public DDerived 
{ 
public: 
    void funcBase() { cout << "base" << endl; } 
    void funcDerived1() { cout << "derived1" << endl; } 
    void funcDerived2() { cout << "derived2" << endl; } 
    void funcDDerived() { cout << "dderived" << endl; } 
}; 

int main() 
{ 
    DDerived *pObject = new Implementation; 
    pObject->funcBase(); 

    return 0; 
} 

La razón por la que escribí este código es para probar si el funcBase función() puede ser llamado en una instancia de DDerived o no. Mi compilador de C++ (Visual Studio 2010) me dio un mensaje de error de compilación cuando intenté compilar este código. En mi opinión, no hay ningún problema en este código porque es cierto que la función funcBase() se implementará (por lo tanto se anulará) en alguna clase derivada de la interfaz DDerived, porque es puramente virtual. En otras palabras, cualquier variable de puntero de tipo Implementation * se debe asociar con una instancia de una clase derivando Implementación y anulando la función funcBase().

Mi pregunta es, ¿por qué el compilador me da tal mensaje de error? Por qué la sintaxis de C++ se define así; es decir, ¿tratar este caso como un error? ¿Cómo puedo hacer que el código se ejecute? Quiero permitir herencia múltiple de interfaces. Por supuesto, si uso "público virtual" o volver a declarar la función funcBase() en Implementation como

interface DDerived : public Derived1, public Derived2 
{ 
    virtual void funcBase() = 0; 
    virtual void funcDDerived() = 0; 
}; 

entonces todo funciona sin ningún problema.

Pero no quiero hacer eso y buscar un método más conveniente, porque la herencia virtual puede degradar el rendimiento, y la nueva declaración es tan tediosa de hacer si las relaciones de herencia de las clases son muy complejas. ¿Hay algún método para habilitar la herencia múltiple de interfaces en C++ que no sea la herencia virtual?

+0

Mis poderes psíquicos me dicen que el error es llamar a la función de clase base ambigua. –

+0

Pero no es una clase base, es una interfaz base o una estructura base. –

+0

posible duplicado de [C++ Herencia múltiple - ¿por qué no trabaja?] (Http://stackoverflow.com/questions/5864466/c-multiple-inheritance-why-you-owork) –

Respuesta

3

El lenguaje C++ está diseñado de tal manera que, en su primer acercamiento sin herencia virtual, habrá dos copias principales del método y no se puede determinar a cuál llamar.

La herencia virtual es la solución de C++ para heredar la misma función desde múltiples bases, por lo que sugeriría simplemente usar ese enfoque.

¿Has considerado alternativamente que no has heredado la misma función de varias bases? ¿Realmente tiene una clase derivada que debe poder tratar como Derived1 o Derived2 O Base dependiendo del contexto?

En este caso, la elaboración de un problema concreto en lugar de un ejemplo artificial puede ayudar a proporcionar un mejor diseño.

+1

Permítame no estar de acuerdo. En la mayoría de los casos en que un OP publica un programa real, las personas comienzan a gritar que deben publicar un SSCCE en su lugar. Este es uno de esos raros casos en los que el OP sí lo hace, y luego usted se queja de _that._ –

+0

@Mr Lister ¡Tiene razón! Desafortunadamente, en este caso, no tengo suficiente contexto (código simplificado lo suficientemente completo) para decir por qué 'Base',' Derived' y 'Derived2' no son todas las clases ortogonales heredadas directamente en' DDerived'. –

1
DDerived *pObject = new Implementation; 
pObject->funcBase(); 

Esto crea un puntero del tipo DDerived a una Implementación. Cuando está utilizando DDerived, realmente solo tiene un puntero a una interfaz.

DDerived no conoce la implementación de funcBase debido a la ambigüedad de tener funcBase definido en Derived1 y Derived2.

Esto ha creado un diamante de herencia que es lo que realmente está causando el problema.

http://en.wikipedia.org/wiki/Diamond_problem

También tuve que comprobar en la "palabra clave" interfaz que tienen allí

es una extensión ms específica que está reconocido por Visual Studio

7

A medida que hemos definido, la estructura de su objeto es el siguiente:

enter image description here

El punto importante aquí es que cada instancia de Implementation contiene dos instancias completamente separadas de Base. Está proporcionando una anulación de Base::funcBase, pero no sabe si está tratando de anular funcBase para el Base que heredó a través de Derived1, o el Base que heredó a través de Derived2.

Sí, la forma más sencilla de lidiar con esto es la herencia virtual. Esto va a cambiar su estructura, de modo que sólo hay una instancia de Base:

enter image description here

Esto es casi sin duda, lo que realmente quiere. Sí, tiene una reputación de problemas de rendimiento en la época de los compiladores primitivos y los de 486 MHz y demás. Con un compilador y procesador moderno, usted es poco probable para encontrar un problema.

Otra posibilidad sería algún tipo de alternativa basada en plantilla, pero que tiende a saturar el resto de tu código, es decir, en lugar de pasar Base *, escribes una plantilla que funcionará con cualquier cosa que proporcione funciones A, B y C, luego pasan (el equivalente de) Implementation como un parámetro de plantilla.

+0

Gracias por la respuesta. Usted dijo eso "pero no sabe si está intentando anular FuncBase para la Base que heredó a través de Derived1, o la Base que heredó a través de Derived2", pero creo que no es el caso; cuando reemplacé "objeto DDerived" a "Implementación * objeto", el compilador ya no se quejó ... – user1361349

+0

Esto puede deberse a que dos copias de funcBase se reemplazan por el mismo método que se describe en Implementación. Del mismo modo, como dije en mi pregunta, cuando agregué "void funcBase() = 0" en la definición de DDerived, no hubo error de compilación. ¿Qué piensas sobre eso? Gracias. – user1361349

1

Creo que C++ Standard 10.1.4 - 10.1.5 puede ayudarlo a comprender el problema en su código.

class L { public: int next; /∗ ... ∗/ }; 
class A : public L { /∗...∗/ }; 
class B : public L { /∗...∗/ }; 
class C : public A, public B { void f(); /∗ ... ∗/ }; 

10.1.4 Un especificador de clase base que no lo hace contienen la palabra clave virtual, especifica una clase base no virtual. Un especificador de clase base que contiene la palabra clave virtual, especifica una clase base virtual. Para cada aparición distinta de una clase base no virtual en la clase celosía de la clase más derivada, el objeto más derivado (1.8) debe contener un subobjeto de clase base distinto correspondiente de ese tipo. Para cada clase base distinta que se especifique virtual, el objeto más derivado contendrá un solo subobjeto de clase base de ese tipo. [Ejemplo: para un objeto de clase tipo C, cada aparición distinta de una clase base (no virtual) L en la clase celosía de C corresponde a uno a uno con un subobjeto L distinto dentro del objeto de tipo C. Dada la clase C definida anteriormente, un objeto de clase C tendrá dos subobjetos de clase L como se muestra a continuación.

10.1.5 En tales redes, calificación explícita se puede utilizar para especificar qué subobjeto se entiende. El cuerpo de la función C :: f podría referirse al miembro al lado de cada subobjeto L: void C :: f() {A :: next = B :: next; } // bien formado. Sin la A :: B :: o calificadores, la definición de C :: f anterior sería mal formados debido a la ambigüedad

Entonces sólo tiene que añadir calificadores al llamar pObject-> funcBase() o resolver la ambigüedad de otra manera.

pObject->Derived1::funcBase(); 

Actualizado: lectura también muy útil será 10.3 Funciones virtuales del estándar.

Tenga un buen fin de semana :)