2011-07-06 18 views
6

Me doy cuenta de lo difícil que pueden ser las preguntas ... Ojalá pueda dar ejemplos que sean ambos lo suficientemente precisos para demostrar mi problema y lo suficientemente cortos como para no estropear todo ... Al menos existe la posibilidad Para editar.C++ Plantilla vs Herencia

Así que este es mi tipo de situación en este momento. Por supuesto que alteré un poco en términos de lógica/estructura (y en términos de nomenclatura de todos modos) tratando de concentrarse en la esencia de mi pregunta:

// MyClass deals with lists (actually several data structures) of the 
// type MyType which should support different types and has to be 
// efficiently dealt with. Templating seems just right here 
class MyClass 
{ 
    ... 

    void doSomething<class MyType>(vector<MyType> someList); 

    ... 

    // At some point I have to extract elements of the type MyType. 
    // The extractor obviously depends on MyType but it is not possible to 
    // Create a general version that could use templates itself 
    // (unless I use a specialization for each possible MyType) 

    // I am stuck between these two alternatives: 

    // Possibility1: 
    // Let the client pass the right extractor and template it. 
    template<class Extractor, class MyType> 
    void extract(const Extractor& extractor, const string& source, 
       vector<MyType>* dest) 
    { 
    extractor.extract(source, dest); 
    } 

    // Possibility2: 
    // Use a member _extractor of some base type that has to be set 
    // to a specialization. The ExtractorBase has a virtual method 
    // template<T> void extract(const string& source, vector<T>* myType) = 0 
    // with no definition that is only defined in subclasses wrt certain 
    // postings. 
    ExtractorBase _extractor; 

    template<class MyType> 
    void extract(const string& source, vector<MyType>* dest) 
    { 
    _extractor.extract(source, dest); 
    } 
} 

Por el momento yo preferiría possibility1, porque yo no' Tengo que meterme con la herencia en Extractor para todas las variantes de MyType y del Extractor asociado que quiero probar en el futuro.

Por otro lado, los extractores pueden requerir código complejo y varios miembros (algo como mapas enormes que mapean ciertas entradas en ciertos valores). Por lo tanto, no habrá ganancia de rendimiento mediante el uso de plantillas. Particularmente usando solo el archivo de cabecera, los extractores y probablemente incluso los funtores que se suponen que están insertados, están fuera de discusión. En el pasado, esto ha sido un fuerte indicador de que la creación de plantillas solo aumentará la complejidad del código (teniendo que lidiar con la creación de instancias, haciendo la vida más difícil para el código del cliente, etc.) y que debería tratar de evitarlo por completo.

¿O hay una tercera posibilidad en la que no pensé en absoluto?

+0

¿Qué quiere decir exactamente por 'extraer'? ¿Es que solo se va a usar el vector ? – vines

+0

No está claro cómo MyClass está involucrado en el proceso de extracción (y por qué extract() es un _method_ de MyClass?) – user396672

Respuesta

2

Es mejor ir con la primera opción . Es más limpio y mantenible.

Debido a sus comentarios, estoy haciendo que usted está haciendo una suposición errónea para la segunda opción:

// The ExtractorBase has a virtual method 
// template<T> void extract(const string& source, vector<T>* myType) = 0; 

NO. Eso no es posible; una función template nunca puede ser virtual. Entonces, para implementar la segunda opción, debes optar por algunas formas sucias y difíciles de mantener, lo cual no es una buena idea.

+0

Creo que Björn significa que la variable _extractor miembro es un puntero y apuntar a una implementación adecuada antes de llamar a la función de plantilla. Es viable, pero no me gusta el despacho virtual cuando se pueden usar plantillas: en general, cuanto más resuelto en tiempo de compilación, mejor. De todos modos, este parece ser un caso clásico de "chúpalo y ve" ... comienza con las plantillas, si causan algunos problemas pasa al despacho virtual ... la API puede ser prácticamente la misma de cualquier manera, por lo que el trabajo involucrado debe ser trivial. –

1

Veo la primera posibilidad como más flexible.

En la segunda posibilidad, no veo el interés del extractor de encapsulación innecesario como miembro de la clase. También tiene más acoplamiento entre MyClass y Extractor, lo cual no es bueno. la plantilla reduce el acoplamiento (de alguna manera), por lo que si puede elegir, es mejor.

1

Tiene una tercera opción, proporcione un constructor para MyType que sepa cómo construirse a partir de un std::string. O mejor aún, un par de iteradores, por lo que si necesita construir una secuencia de MyType s a partir de la cadena, puede usar el rango.

+0

Primero que nada tengo que extraer múltiples MyTypes de una cadena, de modo que solo quede el iterador. Si bien es una gran idea, debo agregar que el extractor depende de mucha información adicional. El mejor ejemplo podría ser un "vocabulario" que mapee cadenas de entrada a ID. Todavía muchas gracias por tu ayuda. –

+0

@ b.buchhold, presumiblemente toda esa información es detalles de implementación interna de 'MyType'? – Nim

0

Esto suena como un caso para el Strategy Pattern - su clase tiene una operación cuya implementación puede variar.

Aquí están las concesiones que veo en los diferentes enfoques.

La solución de plantilla evitará tener que declarar una clase de interfaz abstracta, y usar el vtbl para determinar qué implementación usar. Pero te obligará a bloquear en la aplicación en tiempo de compilación.

La solución de herencia lo forzará a declarar una clase de interfaz abstracta, y tomará el rendimiento de buscar la implementación en el vtbl., pero le permitirá elegir la implementación de extracción en tiempo de ejecución.

Sin saber qué tan crítico es el rendimiento para su aplicación y qué tan flexible debe ser, probablemente elegiría la solución de herencia, ya que me gusta la claridad de definir la interfaz en una clase abstracta y la codificación.