确定多态C ++类的大小

时间:2009-09-11 15:59:54

标签: c++ polymorphism sizeof

使用sizeof运算符,我可以确定任何类型的大小 - 但是如何在运行时动态确定多态类的大小?

例如,我有一个指向Animal的指针,我希望得到它指向的实际对象的大小,如果它是Cat或{{{},则会有所不同1}}。有没有一种简单的方法可以做到这一点,缺少创建虚拟方法Dog并重载它以返回每种特定类型的Animal::size

5 个答案:

答案 0 :(得分:6)

如果您知道可能类型的集合,则可以使用RTTI通过dynamic_cast找出动态类型。如果不这样做,唯一的方法是通过虚函数。

答案 1 :(得分:3)

或者您可以使用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函数添加代码。使用虚拟大小功能,您还需要在每个类中实现此功能。但是,这个功能将会非常简单(完全可以复制n-pasteable,这表明语言可能存在限制,代码设计也存在问题):

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

你可以在基类中将它抽象化,这意味着如果你忘记覆盖这个方法,它将是一个编译器错误。

编辑:这自然是假设给定任何生物你想知道它的大小。如果您有充分的理由相信您正在处理Dog - 或Dog的子类(并且您不关心它是否是子类),那么您自然可以将dynamic_cast用于 ad hoc test。

答案 2 :(得分:3)

如果您能够更改源类的设计,则可以完全用静态多态替换动态多态(使用虚函数)并使用CRTP idiom

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情况),你可以这样做,因为CRTP没有动态多态性的灵活性在运行时解析所需的对象。

通过消除虚函数调用开销,静态多态性还具有更高性能的优势。

如果您不想模板化Base类,或者需要在同一位置(如数组或向量)中保存Base类的不同派生实例,则可以在中间类上使用CRTP并移动多态行为到那个班级(类似于维基百科中的Polymorphic copy construction example):

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上发现了一个类似但更详细的answer,它解释了如果我们进一步派生自上面的派生类(例如class FurtherDerived : public Derived {...}),{{1} }将无法正确报告。他提供了more complex variant代码来克服这个问题。

答案 3 :(得分:0)

我无法相信有人发明了type_id()而不是实现适当的特征....

答案 4 :(得分:0)

一种稍微复杂的方法也行得通,那就是通过 Curiously Recurring Template Pattern 来实现这一点

#include <iostream>

class Base {
public:
    virtual ~Base() {}
    virtual size_t getSize() = 0;
};

template <class T>
class BaseT : public Base {
public:
    size_t getSize() override { return sizeof(T); }
};

class Child : public BaseT<Child> {};

int main()
{
    std::unique_ptr<Base> child(new Child);
    std::cout << child->getSize();
}