web-dev-qa-db-fra.com

Stocker des objets de classe dérivés dans des variables de classe de base

Je voudrais stocker des instances de plusieurs classes dans un vecteur. Puisque toutes les classes héritent de la même classe de base, cela devrait être possible.

Imaginez ce programme:

#include <iostream>
#include <vector>
using namespace std;

class Base
{
    public:
    virtual void identify ()
    {
        cout << "BASE" << endl;
    }
};

class Derived: public Base
{
    public:
    virtual void identify ()
    {
        cout << "DERIVED" << endl;
    }
};

int main ()
{
    Derived derived;

    vector<Base> vect;
    vect.Push_back(derived);

    vect[0].identify();
    return 0;
}

Je m'attendais à ce qu'il affiche "DERIVED", car la méthode "identifier" est virtuelle. Au lieu de cela, "vect [0]" semble être une instance de "Base" et il imprime

BASE

Je suppose que je pourrais écrire mon propre conteneur (probablement dérivé du vecteur) qui est capable de le faire (peut-être en ne tenant que des pointeurs ...). Je voulais juste demander s'il y avait une méthode ish plus C++ pour le faire. ET je voudrais être complètement compatible avec les vecteurs (juste pour plus de commodité si d'autres utilisateurs doivent utiliser mon code).

52
drakide

Ce que vous voyez est Découpage d'objets.
. le vecteur agit simplement comme un objet de la classe Base.

Solution:

Vous devez stocker le pointeur sur l'objet de la classe Base dans le vecteur:

vector<Base*> 

En stockant un pointeur sur la classe Base, il n'y aurait pas de découpage et vous pouvez également obtenir le comportement polymorphe souhaité.
Puisque vous demandez un C++ish Pour ce faire, la bonne approche consiste à utiliser un Smart pointer au lieu de stocker un pointeur brut dans le vecteur. Cela vous permettra de ne pas avoir à gérer manuellement la mémoire, RAII faites cela pour vous automatiquement.

64
Alok Save

Vous rencontrez des tranches. Le vecteur copie l'objet derived, un nouveau de type Base est inséré.

5
Andre

TL; DR: vous ne devez pas hériter d'une classe publiquement copiable/mobile.


Il est en effet possible d'empêcher le découpage d'objets, au moment de la compilation: l'objet de base ne doit pas être copiable dans ce contexte.

Cas 1: une base abstraite

Si la base est abstraite, elle ne peut pas être instanciée et vous ne pouvez donc pas expérimenter le découpage.

Cas 2: une base en béton

Si la base n'est pas abstraite, elle peut être copiée (par défaut). Vous avez deux choix:

  • empêcher complètement la copie
  • autoriser la copie uniquement pour les enfants

Remarque: en C++ 11, les opérations de déplacement provoquent le même problème.

// C++ 03, prevent copy
class Base {
public:

private:
    Base(Base const&);
    void operator=(Base const&);
};

// C++ 03, allow copy only for children
class Base {
public:

protected:
    Base(Base const& other) { ... }
    Base& operator=(Base const& other) { ...; return *this; }
};

// C++ 11, prevent copy & move
class Base {
public:
    Base(Base&&) = delete;
    Base(Base const&) = delete;
    Base& operator=(Base) = delete;
};

// C++ 11, allow copy & move only for children
class Base {
public:

protected:
    Base(Base&&) = default;
    Base(Base const&) = default;
    Base& operator=(Base) = default;
};
5
Matthieu M.

J'utiliserais vector<Base*> pour les stocker. Si tu le dis vector<Base>, un découpage se produira.

Cela signifie que vous devrez supprimer les objets réels vous-même après avoir supprimé les pointeurs de votre vecteur, mais sinon, tout devrait bien se passer.

3
Vlad
// Below is the solution by using vector<Based*> vect,
// Base *pBase , and initialized pBase with
// with the address of derived which is
// of type Derived

#include <iostream>
#include <vector>

using namespace std;

class Base
{

public:

virtual void identify ()
{
    cout << "BASE" << endl;
}
};

class Derived: public Base
{
public:
virtual void identify ()
{
    cout << "DERIVED" << endl;
}
};

int main ()

{
Base *pBase; // The pointer pBase of type " pointer to Base"
Derived derived;
// PBase is initialized with the address of derived which is
// of type Derived

pBase = & derived;
// Store pointer to object of Base class in the vector:
vector<Base*> vect;
// Add an element to vect using pBase which is initialized with the address 
// of derived
vect.Push_back(pBase);
vect[0]->identify();
return 0;
}
0
Van Tran