使用sizeof
运算符,我可以确定任何类型的大小——但是如何在运行时动态确定多态类的大小?
例如,我有一个指向 an 的指针Animal
,我想获取它指向的实际对象的大小,如果它是 aCat
或 a ,这将是不同的Dog
。有没有一种简单的方法可以做到这一点,除了创建一个虚拟方法Animal::size
并重载它以返回sizeof
每个特定类型的方法吗?
使用sizeof
运算符,我可以确定任何类型的大小——但是如何在运行时动态确定多态类的大小?
例如,我有一个指向 an 的指针Animal
,我想获取它指向的实际对象的大小,如果它是 aCat
或 a ,这将是不同的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' ;
}
对于每种新类型,您都需要将代码添加到 animal_size 函数中。使用虚拟大小功能,您还需要在每个类中实现此功能。但是,这个函数会简单得多(完全可以复制粘贴,这表明语言可能存在限制和代码设计问题):
virtual unsigned size() const { return sizeof(*this); }
您可以在基类中将其抽象化,这意味着如果您忘记覆盖此方法,它将是编译器错误。
编辑:这自然是假设给定任何你想知道它的大小的生物。如果您有充分的理由相信您正在处理 Dog - 或 Dog 的子类(并且您不在乎它是否是子类),那么您自然可以使用 dynamic_cast 进行临时测试。
如果您能够更改源类的设计,则可以完全用静态多态替换动态多态(使用虚函数)并使用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
案例),您可以这样做,因为 CRTP 没有动态多态的灵活性来在运行时解析所需的对象。
静态多态性还具有通过消除虚函数调用开销而获得更高性能的优势。
如果您不想模板化 Base 类,或者您需要将 Base 类的不同派生实例保存在同一位置(如数组或向量),则可以在中间类上使用 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() 而不是实现适当的特征....
一种同样可行的稍微复杂的方法是通过奇怪重复的模板模式来实现这一点
#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();
}