Me sorprende la afirmación de que es un error utilizar alguna vez un contenedor estándar de C++ como clase base.¿Hay algún riesgo real derivado de los contenedores C++ STL?
Si no es el abuso de la lengua para declarar ...
// Example A
typedef std::vector<double> Rates;
typedef std::vector<double> Charges;
... entonces, ¿qué es, exactamente, el peligro en la declaración de ...
// Example B
class Rates : public std::vector<double> {
// ...
} ;
class Charges: public std::vector<double> {
// ...
} ;
Las ventajas positivas a B incluyen:
- Habilita la sobrecarga de funciones porque f (la tarifa &) y F (Cargos &) son distintas firmas
- Habilita otras plantillas que ser especializados, debido X <tarifas> y X <Cargos> son tipos distintos
- declaración adelantada es trivial
- depurador probablemente le indica si el objeto es un tarifas o una Cargos
- Si, con el paso del tiempo, las Tarifas y Cargos desarrollan personalidades — un Singleton for Rates, un formato de salida para los Cargos — existe un alcance obvio para que se implemente esa funcionalidad.
Las ventajas positivas a A incluyen:
- no tienen que proporcionar implementaciones triviales de constructores etc
- El compilador de quince años de edad pre-estándar que es el único que va a compilar su legado no estrangula
- Dado que las especializaciones son imposibles, la plantilla X < Las tasas > y la plantilla X Cargas > utilizarán el mismo código, por lo que no habrá ninguna hinchazón sin sentido.
Ambos enfoques son superiores a la utilización de un contenedor de crudo, ya que si los cambios en la implementación de vectores <doble> al vector <flotador>, sólo hay un lugar para cambiar con B y quizá sólo un lugar para cambiar con A (podría ser más, porque alguien puede haber puesto declaraciones typedef idénticas en múltiples lugares).
Mi objetivo es que esta sea una pregunta específica, responable, no una discusión de mejores o peores prácticas. Muestre lo peor que puede suceder como consecuencia de derivarse de un contenedor estándar, que se hubiera evitado utilizando un typedef en su lugar.
Editar:
Sin lugar a dudas, la adición de un destructor de clase de tarifas o cargos de clase sería un riesgo, porque std :: vector no declara su destructor como virtual. No hay destructor en el ejemplo, y no hay necesidad de uno. La destrucción de un objeto Rates o Charges invocará al destructor de la clase base. No hay necesidad de polimorfismo aquí, tampoco. El desafío es mostrar que algo malo sucede como consecuencia del uso de la derivación en lugar de un typedef.
Editar:
consideran este caso de uso:
#include <vector>
#include <iostream>
void kill_it(std::vector<double> *victim) {
// user code, knows nothing of Rates or Charges
// invokes non-virtual ~std::vector<double>(), then frees the
// memory allocated at address victim
delete victim ;
}
typedef std::vector<double> Rates;
class Charges: public std::vector<double> { };
int main(int, char **) {
std::vector<double> *p1, *p2;
p1 = new Rates;
p2 = new Charges;
// ???
kill_it(p2);
kill_it(p1);
return 0;
}
¿Hay cualquier posible error que incluso un usuario arbitrariamente desafortunado podría introducir en el ??? sección que dará como resultado un problema con Cargos (la clase derivada), pero no con Tasas (el typedef)?
En la implementación de Microsoft, el vector <T> se implementa a través de la herencia. vector < T, A > es un derivado público de _Vector_Val < T, A > ¿Debería ser preferible la contención?
El comentario en 'kill_it' es incorrecto. Si el tipo dinámico de 'víctima' no es' std :: vector', entonces 'delete' simplemente invoca un comportamiento indefinido. La llamada a 'kill_it (p2)' hace que esto suceda, por lo que no es necesario agregar nada a la sección '// ???' para que esto tenga un comportamiento indefinido. – Mankarse