Question

À l'aide de l'opérateur sizeof , je peux déterminer la taille de tout type & # 8211; mais comment puis-je déterminer dynamiquement la taille d'une classe polymorphe au moment de l'exécution?

Par exemple, j'ai un pointeur sur un Animal et je souhaite obtenir la taille de l'objet sur lequel il pointe, ce qui sera différent s'il s'agit d'un Cat ou un chien . Existe-t-il un moyen simple de le faire, à part créer une méthode virtuelle Animal :: size et la surcharger pour renvoyer le sizeof de chaque type spécifique?

Était-ce utile?

La solution

Si vous connaissez l'ensemble des types possibles, vous pouvez utiliser RTTI pour connaître le type dynamique en effectuant dynamic_cast . Si vous ne le faites pas, le seul moyen est d'utiliser une fonction virtuelle.

Autres conseils

Vous pouvez aussi utiliser typeid, qui pourrait être plus rapide que dynamic_cast (également avec dynamic_cast, vous pouvez transtyper en types intermédiaires dans la hiérarchie).

Cela semble plutôt mauvais:

#include <iostream>
#include <typeinfo>

class Creature
{
    char x[4];
public:
    virtual ~Creature() {}
};

class Animal: public Creature { char x[8];};

class Bird: public Creature { char x[16]; };

class Dog: public Animal { char x[32]; };

class Cat: public Animal { char x[64]; };

class Parrot: public Bird { char x[128]; };

unsigned creature_size(const Creature& cr)
{
    if (typeid(cr) == typeid(Animal)) {
        return sizeof (Animal);
    }
    else if (typeid(cr) == typeid(Dog)) {
        return sizeof(Dog);
    }
    else if (typeid(cr) == typeid(Cat)) {
        return sizeof(Cat);
    }
    else if (typeid(cr) == typeid(Bird)) {
        return sizeof(Bird);
    }
    else if (typeid(cr) == typeid(Parrot)) {
        return sizeof(Parrot);
    }
    else if (typeid(cr) == typeid(Creature)){
        return sizeof(Creature);
    }
    assert(false && "creature_size not implemented for this type");
    return 0;
}

int main()
{
    std::cout << creature_size(Creature()) << '\n'
    << creature_size(Animal()) << '\n'
    << creature_size(Bird()) << '\n'
    << creature_size(Dog()) << '\n'
    << creature_size(Cat()) << '\n'
    << creature_size(Parrot()) << '\n' ;
}

Pour chaque nouveau type, vous devez ajouter du code à la fonction creature_size. Avec une fonction de taille virtuelle, vous devrez également implémenter cette fonction dans chaque classe. Cependant, cette fonction sera beaucoup plus simple (parfaitement copier-coller, ce qui montre qu’il peut y avoir une limitation dans la langue et un problème de conception de code):

virtual unsigned size() const { return sizeof(*this); }

Et vous pouvez le rendre abstrait dans la classe de base, ce qui signifie que ce sera une erreur du compilateur si vous oubliez de remplacer cette méthode.

Éditer: cela suppose naturellement que, étant donné toute créature, vous voulez connaître sa taille. Si vous avez de bonnes raisons de croire que vous avez affaire à un chien - ou à une sous-classe de chien (et que vous vous en fichez), vous pouvez naturellement utiliser dynamic_cast pour un ad hoc test.

Si vous parvenez à modifier la conception des classes sources, vous pouvez totalement remplacer le polymorphisme dynamique (qui utilise des fonctions virtuelles) par le polymorphisme statique et utiliser le idiome CRTP :

template <class TDerived>
class Base
{
public:
    int getSize()
    { return sizeof(TDerived); }

    void print()
    {
          std::cout
             << static_cast<TDerived*>(this)->getSize()
             << std::endl;
    }

    int some_data;
};

class Derived : public Base<Derived>
{
public:
    int some_other_data1;
    int some_other_data2;
};

class AnotherDerived : public Base<AnotherDerived>
{
public:
    int getSize()
    { return some_unusual_calculations(); }
    // Note that the static_cast above is required for this override to work,
    //  because we are not using virtual functions
};

int main()
{
    Derived d;
    d.print();

    AnotherDerived ad;
    ad.print();

    return 0;
}

Vous pouvez le faire lorsque le comportement polymorphique requis du programme peut être déterminé au moment de la compilation (comme dans le cas sizeof ), car le CRTP ne dispose pas de la souplesse nécessaire. polymorphisme dynamique pour résoudre l'objet souhaité au moment de l'exécution.

Le polymorphisme statique présente également l’avantage d’être plus performant en supprimant la surcharge d’appel de fonction virtuelle.

Si vous ne souhaitez pas modéliser la classe de base ou si vous devez détenir différentes instances dérivées de la classe de base au même emplacement (comme un tableau ou un vecteur), vous pouvez utiliser CRTP sur une classe moyenne et déplacer le comportement polymorphe. vers cette classe (similaire à la Exemple de construction de copie polymorphe dans Wikipedia):

class Base
{
public:
    virtual int getSize() = 0;

    void print()
    {
        std::cout << getSize() << std:endl;
    }

    int some_data;
};

template <class TDerived>
class BaseCRTP: public Base
{
public:
    virtual int getSize()
    { return sizeof(TDerived); }
};

class Derived : public BaseCRTP<Derived>
{
    // As before ...
};

class AnotherDerived : public BaseCRTP<AnotherDerived>
{
    // As before ...

    // Note that although no static_cast is used in print(),
    //  the getSize() override still works due to virtual function.
};

Base* obj_list1[100];
obj_list1[0] = new Derived();
obj_list1[2] = new AnotherDerived();

std::vector<Base*> obj_list2;
obj_list2.push_back(new Derived());
obj_list2.push_back(new AnotherDerived());

-
Mise à jour: Je viens de trouver un réponse similaire mais plus détaillé, ce qui explique que si nous poursuivons Si vous dérivez des classes dérivées ci-dessus (par exemple, classe FurtherDerived: public Derived {...} ), le sizeof ne sera pas correctement consigné. Il fournit une variante plus complexe du code pour y remédier.

Je ne peux pas croire que quelqu'un a inventé type_id () au lieu de mettre en œuvre les traits appropriés ....

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top