Habiendo pasado bastante tiempo desarrollando en C #, noté que si declara una clase abstracta con el propósito de usarla como interfaz, no puede crear una instancia de un vector de esta clase abstracta para almacenar instancias de las clases secundarias.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
La línea que declara el vector de clase abstracta provoca este error en MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Veo una solución obvia, que es reemplazar IFunnyInterface con lo siguiente:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
¿Es esta una solución alternativa aceptable en cuanto a C ++? Si no es así, ¿hay alguna biblioteca de terceros como boost que pueda ayudarme a solucionar esto?
Gracias por leer esto !
Antonio
fuente
No puede crear un vector de un tipo de clase abstracta porque no puede crear instancias de una clase abstracta y contenedores de la biblioteca estándar de C ++ como std :: vector store values (es decir, instancias). Si quiere hacer esto, tendrá que crear un vector de punteros al tipo de clase abstracta.
Su solución alternativa no funcionaría porque las funciones virtuales (que es la razón por la que desea la clase abstracta en primer lugar) solo funcionan cuando se llaman a través de punteros o referencias. Tampoco puede crear vectores de referencias, por lo que esta es una segunda razón por la que debe usar un vector de punteros.
Debe darse cuenta de que C ++ y C # tienen muy poco en común. Si tiene la intención de aprender C ++, debe pensar en ello como comenzar desde cero y leer un buen tutorial dedicado de C ++ como Accelerated C ++ de Koenig y Moo.
fuente
En este caso, no podemos usar ni siquiera este código:
std::vector <IFunnyInterface*> funnyItems;
o
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Porque no existe una relación IS A entre FunnyImpl e IFunnyInterface y no hay conversión implícita entre FUnnyImpl e IFunnyInterface debido a la herencia privada.
Debe actualizar su código de la siguiente manera:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
fuente
La alternativa tradicional es usar una
vector
de punteros, como ya se señaló.Para aquellos que lo aprecian,
Boost
viene con una biblioteca muy interesante:Pointer Containers
que se adapta perfectamente a la tarea y lo libera de los diversos problemas que implican los punteros:Tenga en cuenta que esto es significativamente mejor que uno
vector
de los punteros inteligentes, tanto en términos de rendimiento como de interfaz.Ahora, hay una tercera alternativa, que es cambiar su jerarquía. Para un mejor aislamiento del usuario, he visto varias veces el siguiente patrón utilizado:
class IClass; class MyClass { public: typedef enum { Var1, Var2 } Type; explicit MyClass(Type type); int foo(); int bar(); private: IClass* m_impl; }; struct IClass { virtual ~IClass(); virtual int foo(); virtual int bar(); }; class MyClass1: public IClass { .. }; class MyClass2: public IClass { .. };
Esto es bastante sencillo y una variación del
Pimpl
idioma enriquecido por unStrategy
patrón.Funciona, por supuesto, sólo en el caso en el que no desee manipular los objetos "verdaderos" directamente, e implica una copia profunda. Así que puede que no sea lo que deseas.
fuente
Porque para cambiar el tamaño de un vector es necesario utilizar el constructor predeterminado y el tamaño de la clase, que a su vez requiere que sea concreto.
Puede utilizar un puntero como se sugiere.
fuente
std :: vector intentará asignar memoria para contener su tipo. Si su clase es puramente virtual, el vector no puede conocer el tamaño de la clase que tendrá que asignar.
Creo que con su solución, podrá compilar un
vector<IFunnyInterface>
pero no podrá manipular FunnyImpl dentro de él. Por ejemplo, si IFunnyInterface (clase abstracta) es de tamaño 20 (realmente no lo sé) y FunnyImpl es de tamaño 30 porque tiene más miembros y código, terminarás intentando encajar 30 en tu vector de 20La solución sería asignar memoria en el montón con "nuevo" y almacenar punteros en
vector<IFunnyInterface*>
fuente
Creo que la causa principal de esta limitación realmente triste es el hecho de que los constructores no pueden virtual. Por lo tanto, el compilador no puede generar código que copie el objeto sin conocer su tiempo en el tiempo de compilación.
fuente