2012-04-14 41 views
6
código

Referencia:C++ vector con la herencia

#include <vector> 
#include <iostream> 

class Func { 
public: 
    virtual void call() { 
     std::cout<< "Func -> call()" << std::endl; 
    } 
}; 

class Foo : public Func { 
public: 
    void call() { 
     std::cout<< "Foo -> call()" << std::endl; 
    } 
}; 

class Bar : public Func { 
public: 
    void call() { 
     std::cout<< "Bar -> call()" << std::endl; 
    } 
}; 

int main(int argc, char** argv) { 
    std::vector<Func> functors; 

    functors.push_back(Func()); 
    functors.push_back(Foo()); 
    functors.push_back(Bar()); 

    std::vector<Func>::iterator iter; 
    for (iter = functors.begin(); iter != functors.end(); ++iter) 
     (*iter).call(); 
} 

Al ejecutar ese código, se produce el siguiente resultado en mi equipo:

$ ./test 
Func -> call() 
Func -> call() 
Func -> call() 

Habría alguna manera de asegurar que la función virtual correcta se llama en este caso? Soy nuevo en C++, pero mi mejor conjetura es que aquí:

(*iter).call(); 

Se trata de ser arrojados a un objeto Func. ¿Es esto correcto?

+0

posible duplicado de [Almacenar dos clases con la misma clase base en un std :: vector] (http://stackoverflow.com/questions/8777724/store-two-classes-with-the-same-base-class -in-a-stdvector) –

+0

sí, sin embargo no pude encontrar esa publicación antes de crear ésta, lo siento, mi búsqueda-fu no debe ser tan buena como la suya señor –

+0

En su caso, está ocurriendo un corte de objetos, entonces la parte derivada se astilla. Debería utilizar Punteros de clase base para Almacenamiento de objetos heterogéneos. – Naveen

Respuesta

6

Debe usar un shared_ptr o unique_ptr para contener los elementos de una colección de tipo polimórfico.

Como su código está escrito ahora, las instancias de Foo y Bar se cooperced (copia construida) en una instancia de tipo Func para encajar en el vector. (La razón es que el vector almacena sus elementos inmediatamente por valor de tamaño fijo para el rendimiento; sin embargo, las subclases polimórficas son de un tamaño arbitrariamente mayor desconocido en tiempo de compilación, por lo que solo puede almacenar la clase base.)

Esto es mejor :

int main(int argc, char** argv) { 
    vector<shared_ptr<Func>> functors; 

    functors.push_back(make_shared<Func>()); 
    functors.push_back(make_shared<Foo>()); 
    functors.push_back(make_shared<Bar>()); 

    for (auto functor : functors) 
     functor->call(); 
} 

en lo anterior un puntero de referencia contado se utiliza para compartir implícitamente las subclases hetrogeneous de Func en el vector. (Esta indirección permite que las subclases de Func de tamaño arbitrario se almacenen por direccionamiento indirecto.)

Además, es posible que desee echar un vistazo a std :: function y std :: bind, en lugar de rodar su propio tipo de functor.

Otra cosa a tener en cuenta sería un reenvío perfecto y plantillas varádicas.

actualización: Por edad compilador:

int main(int argc, char** argv) { 
    vector<std::tr1::shared_ptr<Func> > functors; 

    functors.push_back(std::tr1::make_shared<Func>()); 
    functors.push_back(std::tr1::make_shared<Foo>()); 
    functors.push_back(std::tr1::make_shared<Bar>()); 

    for (size_t i = 0; i < functors.size(); ++i) 
     functors[i]->call(); 
} 
+0

+1 por sugerir mirar en std :: function y enlazar – lurscher

+0

Parece que no puedo obtener este código para compilar, ¿este código de refuerzo o std C++? mis compiladores: 'i686-manzana-darwin11-llvm-g ++ - 4.2 (GCC) 4.2.1 (Sobre la base de Apple Inc. construir 5658) (LLVM construir 2336.9.00) de Apple versión 3.1 (sonido metálico etiquetas/de Apple /clang-318.0.58) (basado en LLVM 3.1svn) ' –

+0

@RonElliott: lo siento, el código anterior usa el estándar actual de C++ (2011). Escribiré una versión con el viejo estándar, espera. –

1

su std :: vector es el almacenamiento de objetos Func - esto significa que cuando se llama a

functors.push_back(Foo()); 
functors.push_back(Bar()); 

va a crear Foo y Bar objetos , luego "cortar" esos objetos a medida que se copian en objetos Func.

Si desea utilizar Foo y Bar polimórfica, a continuación, un patrón más típico sería para almacenar un vector de algún tipo puntero (Preferiblemente no punteros "en bruto" sin embargo), por ejemplo

std::vector< std::unique_ptr<Func> >

std::vector< std::shared_ptr<Func> >

O, si realmente tiene que ..(Pero sólo si está utilizando un compilador antiguo que no disponga de shared_ptr o unique_ptr)

std::vector< Func* >

2

el vector sólo se mantiene el tipo Func por valor, lo que significa que todas las cosas temporales Foo y se cortan en rodajas y Bar fundido a la base Func tipo

necesita cambiar a algo así como std::vector< Func* > y asignar dinámicamente las instancias clases de derivados con el fin para el envío polimórfica a trabajar

Si yo u está completamente seguro de que no va a pasar este vector para otras funciones después devuelto por esta función, como una optimización es posible que desee asignar los casos en la pila:

std::vector< Func* > v; 
Bar b; 
Foo f; 
v.push_back(&b); 
v.push_back(&f); 
+0

Esto funciona, sí, puedo estar seguro de que este vector no se pasará, así es como planeé implementarlo –

1

En C++ polimorfismo sólo funciona con punteros y referencias , mientras que el vector almacena directamente instancias de objetos. Cuando llama al push_back se llama al constructor de copia Func, que crea el objeto Func que está almacenado dentro del vector.

Esto se denomina segmentación de objetos, puede obtener más información al respecto con una búsqueda rápida en StackOverflow.

La solución sería almacenar punteros (o, mejor aún, punteros inteligentes) a sus objetos, que deberían asignarse en otro lugar (probablemente en el montón).

0

Su problema es que usted tiene un vector de Func, pero los métodos se llaman polimórficamente solo a través de referencias o punteros.

1

En general, las instancias de subclases pueden ser más grandes que las de su superclase, por lo que no debe esperar que las subclases quepan en la ranura del vector.

Y push_back probablemente llamar internamente un constructor de copia (de la clase Func, ya que tienes vector<Func>) por lo que las ranuras de vectores internos son de hecho Func no de otras clases.