演算子を使用するsizeof
と、任意の型のサイズを決定できますが、実行時にポリモーフィック クラスのサイズを動的に決定するにはどうすればよいでしょうか?
たとえば、 へのポインタがありAnimal
、それが指している実際のオブジェクトのサイズを取得したいのですが、それが か である場合は異なりCat
ますDog
。これを行う簡単な方法はありますか? 仮想メソッドを作成し、Animal::size
それをオーバーロードして特定の各型の を返す以外に方法はありsizeof
ますか?
演算子を使用するsizeof
と、任意の型のサイズを決定できますが、実行時にポリモーフィック クラスのサイズを動的に決定するにはどうすればよいでしょうか?
たとえば、 へのポインタがありAnimal
、それが指している実際のオブジェクトのサイズを取得したいのですが、それが か である場合は異なりCat
ますDog
。これを行う簡単な方法はありますか? 仮想メソッドを作成し、Animal::size
それをオーバーロードして特定の各型の を返す以外に方法はありsizeof
ますか?
可能なタイプのセットがわかっている場合は、RTTI を使用して動的なタイプを見つけることができます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;
}
CRTPには、実行時に目的のオブジェクトを解決するための動的ポリモーフィズムの柔軟性がないため、プログラムの必要なポリモーフィック動作をコンパイル時に決定できる場合(この場合のように)、これを行うことができます。sizeof
静的ポリモーフィズムには、仮想関数呼び出しのオーバーヘッドを削除することにより、パフォーマンスが向上するという利点もあります。
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
が正しくレポートされないことを説明しています。彼はこれを克服するためにコードのより複雑な変形を与えます。
または、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' ;
}
新しいタイプごとに、creative_size 関数にコードを追加する必要があります。仮想サイズ関数では、この関数を各クラスにも実装する必要があります。ただし、この関数は非常に単純になります (完全にコピー アンド ペースト可能であり、言語の制限とコード設計の問題の両方が存在する可能性があることを示しています)。
virtual unsigned size() const { return sizeof(*this); }
また、基本クラスで抽象化できます。つまり、このメソッドをオーバーライドするのを忘れると、コンパイラ エラーになります。
編集:これは、クリーチャーのサイズを知りたい場合に当然のことです。Dog または Dog のサブクラスを扱っていると信じる強い理由がある場合 (そしてそれがサブクラスであるかどうかは気にしない場合) は、当然のことながら、その場しのぎのテストに dynamic_cast を使用できます。