Pergunta

Usando o operador sizeof, posso determinar o tamanho de qualquer tipo - mas como posso dinamicamente determinar o tamanho de uma classe polimórfica em tempo de execução?

Por exemplo, eu tenho um ponteiro para um Animal, e eu quero começar o tamanho do objeto real ele aponta, que será diferente se for um Cat ou um Dog. Existe uma maneira simples de fazer isso, longe de criar um método Animal::size virtual e sobrecarregando-a para retornar o sizeof de cada tipo específico?

Foi útil?

Solução

Se você sabe que o conjunto de tipos possíveis, você pode usar RTTI para descobrir o tipo dinâmico, fazendo dynamic_cast. Se não o fizer, a única maneira é através de uma função virtual.

Outras dicas

Ou você pode usar typeid, que pode ser mais rápido do que dynamic_cast (também com dynamic_cast você pode lançar a tipos intermediários na hierarquia).

Parece bastante ruim:

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

Para cada novo tipo você precisará adicionar código para a função creature_size. Com uma função de tamanho virtual que você precisa para implementar esta função em cada classe também. No entanto, esta função será significativamente mais simples (perfeitamente cópia-n-pasteable, o que mostra que pode haver tanto uma limitação na língua e um problema com seu projeto de código):

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

E você pode torná-lo abstrata na classe base que significa que ele será um erro do compilador se você esquecer de substituir esse método.

Edit: este é, naturalmente, assumindo que dado qualquer criatura que você quer saber seu tamanho. Se você tem uma forte razão para acreditar que você está lidando com um cão - ou uma subclasse de cão (e você não se importa se ele é uma subclasse), então, naturalmente, você pode usar dynamic_cast para um ad hoc teste.

Se você é capaz de projetar as classes de origem mudança, você pode substituir totalmente o polimorfismo dinâmico (que utiliza funções virtuais) com polimorfismo estático e usar o CRTP idioma :

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

Você pode fazer isso quando o necessário comportamento polimórfico de programa pode ser determinado em tempo de compilação (como o caso sizeof), uma vez que o CRTP não tem a flexibilidade de polimorfismo dinâmica para resolver o objeto desejado em tempo de execução.

O polimorfismo estático também tem a vantagem de maior desempenho, removendo-função-chamada virtual em cima.

Se você não quer templatize Classe base ou você precisa para realizar diferentes instâncias derivadas da classe base em um mesmo local (como uma matriz ou um vetor), você pode usar CRTP em uma classe média e mover o comportamento polimórfico a essa classe (semelhante ao polimórfica construção cópia exemplo na 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());

-
Atualização: I encontrado agora uma resposta semelhante, mas mais detalhado sobre stackoverflow que explica que, se ainda derivam das classes derivadas acima (por exemplo class FurtherDerived : public Derived {...}), o sizeof não irá reportar correctamente. Ele dá um variante mais complexa do código para superar isso.

Eu não posso acreditar type_id inventou essa de alguém () em vez de implementar características adequadas ....

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top