2012-01-03 12 views
8

Normalmente programo en C# pero estoy tratando de hacer un poco de C++ y estoy luchando de alguna manera tratando de implementar interfaces en C++.Implementando interfaces en C++

En C# que me gustaría hacer algo como esto:

class Base<T> 
{ 
    public void DoSomething(T value) 
    { 
     // Do something here 
    } 
} 

interface IDoubleDoSomething 
{ 
    void DoSomething(double value); 
} 

class Foo : Base<double>, IDoubleDoSomething 
{ 
} 

En C++ He aplicado de esta manera:

template <class T> 
class Base 
{ 
public: 
    virtual void DoSomething(T value) 
    { 
     // Do something here 
    } 
}; 

class IDoubleDoSomething 
{ 
public: 
    virtual void DoSomething(double value) = 0; 
}; 

class Foo : public Base<double>, public IDoubleDoSomething 
{ 
}; 

El problema es que yo no puede crear una instancia de Foo, ya que es Resumen (no implementa DoSomething). Me doy cuenta de que puedo implementar DoSomething y solo llamar al método en Base, pero esperaba que hubiera una forma mejor de hacerlo. Tengo otras clases que heredan de base con diferentes tipos de datos y tengo otras clases que heredan de IDoubleDoSomething que no usan Base.

Cualquier ayuda apreciada.

+2

¿Qué es tan malo con esto, y de qué manera podría ser "mejor"? –

+2

Supongo que "mejor" estaría actuando como la versión C# donde 'Foo' hereda el método de Base y no necesita ninguna implementación. Supongo que no hay nada realmente malo en hacer una implementación de cada método desde Base en Foo, simplemente se siente desordenado. – Clueless

+0

Es complicado, pero nunca he encontrado una mejor manera :( –

Respuesta

1

Como han notado otros, las dos funciones en las clases base no están relacionadas (a pesar de tener el mismo nombre y tipos de argumentos), ya que no tienen una clase base común. Si desea que se relacionen, debe darles una clase base común.

Además, en general, si desea que la herencia múltiple funcione correctamente, debe declarar sus clases base no privadas como virtual. De lo contrario, si alguna vez tienes clases base comunes (como a menudo necesitas para este estilo de código), sucederán cosas malas.

Así que teniendo en cuenta que, usted puede hacer su trabajo de ejemplo de la siguiente manera:

template <class T> 
class IDoSomething { 
public: 
    virtual void DoSomething(T value) = 0; 
}; 

template <class T> 
class Base : public virtual IDoSomething<T> 
{ 
public: 
    virtual void DoSomething(T value) 
    { 
     // Do something here 
    } 
}; 

class IDoubleDoSomething : public virtual IDoSomething<double> 
{ 
}; 

class Foo : public virtual Base<double>, public virtual IDoubleDoSomething 
{ 
}; 
+0

Esto parece ser lo más cercano a lo que estaba tratando de lograr y, dado que soy el propietario de la 'Base', funciona bien para mi escenario. Si yo no fuera el propietario de 'Base', entonces supongo que las otras soluciones aquí serían más aplicables. – Clueless

2

Consideremos el siguiente código C++

class Foo 
{ 
public: 
    void DoSomething1(){} 
}; 

template<typename t> 
void MethodExpectsDosomething1(t f) 
{ 
    f.DoSomething1(); 
} 

template<typename t> 
void MethodExpectsDosomething2(t f) 
{ 
    f.DoSomething2(); 
} 

int main() 
{ 
    Foo f; 
    MethodExpectsDosomething1<Foo>(f); 

    MethodExpectsDosomething2<Foo>(f); 

    return 0; 
} 

En C++ se puede utilizar Foo sin ella y la implementación de un IDoSomething1IDoSomething2. El segundo método MethodExpectsDosomething2 simplemente no se compilará porque Foo no tiene el método DoSomething2.

En C# tal construcción no es posible y obliga a tener IDoSomething1 y IDoSomething2 interfaz y especificar que como type constraint.

Así que tal vez necesite ver su código y ver si tales interfaces son necesarias en absoluto?

+0

el problema con el tipado de pato es que no es autodocumentado, mientras que la solución que involucra el polimorfismo ES auto-documentada. – akappa

+0

Si fue tan pato-tipado mal ... C# 4.0 no lo habría introducido como palabra clave 'dynamic'. El hecho de que te den un hacha no significa que tengas que cortarte las piernas –

+0

, puedo recordarte que el argumento" es popular, por lo que no puede ser cualquier mal "¿no es bueno? Si eres el usuario de una función de plantilla, ¿cómo averiguas qué tipo de funcionalidades que funcionan solicitan de tu objeto y con qué semántica? Este problema no existe con el polimorfismo dinámico, dado que la interfaz ES la documentación. – akappa

-1

Siempre que todo lo demás esté en orden, esto debería funcionar.

class Foo : public Base<double> 
{ 
}; 

En su Foo código original habría 2 DoSomething vacío (valor doble) métodos (1 siendo abstracta). Eso sería una parte base y una parte IDoubleDoSomething. Foo :: Base y Foo :: IDoubleDoSomething. Puede intentarlo dando e implementando temporalmente a IDoubleDoSomething.DoSomething().

Pero como Foo ya "es una" base" que tiene lo que necesita sin IDoubleDoSomething

+2

La razón para usar una interfaz es pasarle un puntero sin requerir la definición de la clase base o derivada. La eliminación de la clase de interfaz va hacia atrás. –

2

En C++, las funciones virtuales puras deben ser siempre reemplaza en una clase derivada;. No pueden heredar las anulaciones de otra base clases como esa. Si necesita polimorfismo dinámico, no creo que haya ninguna alternativa sensata para escribir una función en Foo que llame a la función Base. Tenga en cuenta que la función Base no necesita ser virtual.

Dependiendo de cómo esté utilizando estas clases (en particular, si el tipo real de cada instancia de la interfaz se conoce en tiempo de compilación), puede utilizar el polimorfismo estático para inyectar su clase de implementación particular en su usuario como un parámetro de plantilla; por ejemplo:

// No explicit interface specification with static polymorphism 
class Foo : public Base<double> 
{ 
    // Inherits DoSomething(double) 
}; 

// Template can used with any class that impelements a 
// "DoSomething" function that can accept a double. 
template <class DoubleDoSomething> 
void DoSomethingWithDouble(DoubleDoSomething & interface, double value) 
{ 
    interface.DoSomething(value); 
} 

// This particular specialisation uses a "Foo" object. 
Foo foo; 
DoSomethingWithDouble(foo, 42); 
2

La cosa es, Base::DoSomething y IWhatever::DoSomething son dos funciones no relacionadas (incluso si no había funciones virtuales puras en esto, no sería capaz de llamar DoSomething en un objeto Foo, de todos modos) Base necesita heredar de IWhatever para que esto funcione.

Dicho esto, pregúntese si realmente necesita esto. La programación genérica con plantillas (y conceptos, que son una especie de interfaces, consulte Boost.ConceptCheck) suele ser una mejor solución en C++ que el polimorfismo del subtipo en tiempo de ejecución.

2

Usted podría pasar a un segundo parámetro de plantilla a la base, la interfaz que es para poner en práctica:

template <typename T, class Interface> 
class Base : public Interface { ... }; 

class Foo : public Base<double, IDoubleDoSomething> { ... }; 

Para los puntos de bonificación extra que podría templatise IDoubleDoSomething (por lo que es por ejemplo IDoSomething<double>), o utilizar una clase de rasgos de mapear el tipo T a la interfaz relevante.

Cuestiones relacionadas