Определение размера полиморфного класса C ++

StackOverflow https://stackoverflow.com/questions/1411806

  •  05-07-2019
  •  | 
  •  

Вопрос

Используя sizeof оператор, я могу определить размер любого типа - но как я могу динамически определять размер полиморфного класса во время выполнения?

Например, у меня есть указатель на Animal, и я хочу получить размер фактического объекта, на который он указывает, который будет отличаться, если это Cat или Dog.Есть ли простой способ сделать это, если не считать создания виртуального метода Animal::size и перегружаю его, чтобы вернуть sizeof каждого конкретного типа?

Это было полезно?

Решение

Если вам известен набор возможных типов, вы можете использовать RTTI для определения динамического типа, выполнив dynamic_cast . Если вы этого не сделаете, единственный путь через виртуальную функцию.

Другие советы

Или вы можете использовать typeid, который может быть быстрее, чем dynamic_cast (также с dynamic_cast вы можете приводить к промежуточным типам в иерархии).

Это выглядит довольно плохо:

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

Для каждого нового типа вам нужно добавить код в функцию creature_size. С функцией виртуального размера вам нужно будет реализовать эту функцию также в каждом классе. Однако эта функция будет значительно проще (идеально копируемо-вставляемой, что показывает, что могут быть как ограничения в языке, так и проблемы с дизайном кода):

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

И вы можете сделать его абстрактным в базовом классе, что означает, что это будет ошибкой компилятора, если вы забудете переопределить этот метод.

Редактировать: естественно, предполагается, что для любого Существа вы хотите знать его размер. Если у вас есть веские основания полагать, что вы имеете дело с Dog - или с подклассом Dog (и вам все равно, является ли он подклассом), то, естественно, вы можете использовать dynamic_cast для ad hoc test.

Если вы можете изменить дизайн исходных классов, вы можете полностью заменить динамический полиморфизм (который использует виртуальные функции) статическим полиморфизмом и использовать Идиома 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;
}

Вы можете сделать это, когда потребуется полиморфное поведение программы может быть определен во время компиляции (например, sizeof case), поскольку CRTP не обладает гибкостью динамического полиморфизма для разрешения нужного объекта во время выполнения.

Статический полиморфизм также имеет преимущество в повышении производительности за счет устранения накладных расходов на вызов виртуальной функции.

Если вы не хотите шаблонизировать базовый класс или вам нужно хранить разные производные экземпляры базового класса в одном месте (например, массив или вектор), вы можете использовать CRTP для среднего класса и перенести полиморфное поведение в этот класс (аналогично Пример построения полиморфной копии в Википедии):

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());

--
Обновить: Теперь я нашел похожий, но более подробный ответ о stackoverflow, который объясняет, что если мы в дальнейшем будем извлекать производные из приведенных выше классов (например class FurtherDerived : public Derived {...}), тот sizeof не будет корректно отчитываться.Он дает более сложный вариант из кода, позволяющего преодолеть это.

Я не могу поверить, что кто-то изобрел type_id () вместо реализации правильных черт ....

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top