clases - pure virtual function c++
¿Por qué no podemos declarar un std:: vector<AbstractClass>? (7)
Después de haber pasado bastante tiempo desarrollando C #, noté que si declaras una clase abstracta con el propósito de usarla como interfaz, no puedes instanciar 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 causa 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 esto una solución aceptable en cuanto a C ++? Si no, ¿hay alguna biblioteca de terceros como impulso que podría ayudarme a solucionar esto?
Gracias por leer esto !
Antonio
Creo que la causa raíz de esta limitación realmente triste es el hecho de que los constructores no pueden virtual. A partir de esto, el compilador no puede generar código que copie el objeto sin conocer su hora en el tiempo de compilación.
Debido a que para cambiar el tamaño de un vector, debe usar el constructor predeterminado y el tamaño de la clase, lo que a su vez requiere que sea concreto.
Puede usar un puntero como otros sugirieron.
En este caso, no podemos usar incluso este código:
std::vector <IFunnyInterface*> funnyItems;
o
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Debido a que no existe una relación de IS A entre FunnyImpl e IFunnyInterface y no existe una 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>";
}
};
La alternativa tradicional es usar un vector
de punteros, como ya se señaló.
Para aquellos que aprecian, Boost
viene con una biblioteca muy interesante: Pointer Containers
que se adapta perfectamente a la tarea y te libera de los diversos problemas que implican los punteros:
- gestión de por vida
- doble desreferencia de iteradores
Tenga en cuenta que esto es significativamente mejor que un vector
de punteros inteligentes, tanto en términos de rendimiento como de interfaz.
Ahora, hay una tercera alternativa, que es cambiar tu 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 modismo Pimpl
enriquecido por un patrón de Strategy
.
Funciona, por supuesto, solo en el caso en que no desee manipular los objetos "verdaderos" directamente, e involucra copia en profundidad. Entonces puede que no sea lo que deseas.
No puede crear instancias de clases abstractas, por lo tanto, un vector de clases abstractas no puede funcionar.
Sin embargo, puede usar un vector de punteros para clases abstractas:
std::vector<IFunnyInterface*> ifVec;
Esto también le permite utilizar el comportamiento polimórfico: incluso si la clase no fuera abstracta, almacenarla por valor llevaría al problema de corte de objetos .
No puede crear un vector de un tipo de clase abstracta porque no puede crear instancias de una clase abstracta, y los contenedores de la Biblioteca estándar de C ++ como valores de tienda std :: vector (es decir, instancias). Si desea hacer esto, deberá crear un vector de punteros al tipo de clase abstracta.
Su workround 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.
Debes darte cuenta de que C ++ y C # tienen muy poco en común. Si tiene la intención de aprender C ++, debe pensar que comienza desde cero y leer un buen tutorial dedicado de C ++ como Accelerated C ++ de Koenig y Moo.
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á tratando de encajar 30 en su vector de 20
La solución sería asignar memoria en el montón con "nuevo" y almacenar punteros en el vector<IFunnyInterface*>