Domanda

Usando l'operatore sizeof , posso determinare la dimensione di qualsiasi tipo - ma come posso determinare dinamicamente la dimensione di una classe polimorfica in fase di esecuzione?

Ad esempio, ho un puntatore a un Animal e voglio ottenere la dimensione dell'oggetto reale a cui punta, che sarà diverso se si tratta di un Cat o un Cane . C'è un modo semplice per farlo, a meno di creare un metodo virtuale Animal :: size e sovraccaricarlo per restituire sizeof di ogni tipo specifico?

È stato utile?

Soluzione

Se conosci l'insieme dei tipi possibili, puoi utilizzare RTTI per scoprire il tipo dinamico facendo dynamic_cast . In caso contrario, l'unico modo è tramite una funzione virtuale.

Altri suggerimenti

Oppure puoi usare typeid, che potrebbe essere più veloce di dynamic_cast (anche con dynamic_cast puoi trasmettere a tipi intermedi nella gerarchia).

Sembra piuttosto male:

#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' ;
}

Per ogni nuovo tipo dovrai aggiungere il codice alla funzione creature_size. Con una funzione di dimensione virtuale dovrai implementare questa funzione anche in ogni classe. Tuttavia, questa funzione sarà significativamente più semplice (perfettamente copia-incollabile, il che dimostra che potrebbe esserci sia una limitazione nella lingua che un problema con la progettazione del codice):

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

E puoi renderlo astratto nella classe base, il che significa che sarà un errore del compilatore se dimentichi di scavalcare questo metodo.

Modifica: questo naturalmente presuppone che, data qualsiasi Creatura, tu voglia conoscere le sue dimensioni. Se hai una forte ragione per credere di avere a che fare con un Cane - o una sottoclasse di Cane (e non ti importa se si tratta di una sottoclasse), puoi naturalmente usare dynamic_cast per un ad hoc test.

Se sei in grado di cambiare il design delle classi di origine, puoi sostituire totalmente il polimorfismo dinamico (che utilizza funzioni virtuali) con il polimorfismo statico e utilizzare linguaggio di 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;
}

Puoi farlo quando il comportamento polimorfico necessario del programma può essere determinato in fase di compilazione (come il caso sizeof ), poiché il CRTP non ha la flessibilità di polimorfismo dinamico per risolvere l'oggetto desiderato in fase di esecuzione.

Il polimorfismo statico ha anche il vantaggio di prestazioni più elevate rimuovendo l'overhead di chiamata di funzione virtuale.

Se non si desidera modellare la classe Base o è necessario conservare diverse istanze derivate della classe Base in una stessa posizione (come una matrice o un vettore), è possibile utilizzare CRTP su una classe media e spostare il comportamento polimorfico a quella classe (simile al Esempio di costruzione di copie polimorfiche in 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());

-
Aggiornamento: ora ho trovato una risposta simile ma più dettagliata su stackoverflow che spiega che se noi derivano dalle classi derivate sopra (es. class FurtherDerived: public Derived {...} ), la sizeof non riporterà correttamente. Fornisce una variante più complessa del codice per ovviare a questo.

Non riesco a credere che qualcuno abbia inventato type_id () invece di implementare tratti propri ....

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top