views:

277

answers:

3

Using the sizeof operator, I can determine the size of any type – but how can I dynamically determine the size of a polymorphic class at runtime? For example, I have a pointer to an Animal, and I want to get the size of the actual object it points to, which will be different if it is a Cat or a Dog. Is there a simple way to do this, short of creating a virtual method Animal::size and overloading it to return the sizeof of each specific type?

+5  A: 

If you know the set of possible types, you can use RTTI to find out the dynamic type by doing dynamic_cast. If you don't, this won't work.

sbi
+1  A: 

Or you can use typeid, which might be faster than dynamic_cast (also with dynamic_cast you can cast to intermediary types in the hierarchy).

It looks rather bad:

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

For each new type you'll need to add code to the creature_size function. With a virtual size function you'll need to implement this function in each class as well. However, this function will be significantly simpler (perfectly copy-n-pasteable, which shows there might be both a limitation in the language and a problem with your code design):

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

And you can make it abstract in the base class which means that it will be a compiler error if you forget to override this method.

Edit: this is naturally assuming that given any Creature you want to know its size. If you have a strong reason to believe that you are dealing with a Dog - or a subclass of Dog (and you don't care if it is a subclass), then naturally you can use dynamic_cast for an ad hoc test.

UncleBens
Worse than looking bad, every time you create a new animal, you have to modify creature_size.
Michael Kohne
I very much doubt that there are implementations where `dynamic_cast` and `typeid` do not, under the hood, basically just call into the same code (with `dynamic_cast` wrapping some checks around that, which you did manually). Given that RTTI on some systems (e.g., Windows when DLLs are involved) comes down to string comparisons, _if_ there are any differences between `dynamic_cast` and `typeid` at all, they will most likely be neglectable.
sbi
@Michael: I mentioned that. You'll have to modify your code if you were to use dynamic_cast too. It will be even worse: because dynamic_cast can successfully cast to intermediate types (e.g a Parrot from Creature to Bird), you'll need to be more careful how you order those comparisons! And precisely for this reason that dynamic_cast can achieve this, it might be worse (I've read that typeid does a single comparison, whereas dynamic_cast actually has to search through the inheritance tree.)
UncleBens
@UncleBens: You do have a valid point there. I hadn't thought about that.
sbi
both dynamic cast and typeid use string compares. this has to be done since in most cases for it to work across assembly boundaries. (linking in libs, DLLs etc). If you want performance, stay away from these altogether.
Marius
A: 

I can't believe that somebody's invented type_id() instead of implementing proper traits ....