質問
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); }
そして、基本クラスで抽象化することができます。つまり、このメソッドをオーバーライドするのを忘れると、コンパイラエラーになります。
編集:これは当然、Creatureが与えられた場合、そのサイズを知りたいと想定しています。 Dogを扱っていると信じる強い理由がある場合-またはDogのサブクラス(およびサブクラスであるかどうかは気にしません)であれば、当然、 ad hoc に対してdynamic_castを使用できます em>テスト。
ソースクラスのデザインを変更できる場合は、(仮想関数を使用する)動的ポリモーフィズムを静的ポリモーフィズムに完全に置き換え、 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には柔軟性がないためです。実行時に目的のオブジェクトを解決するための動的ポリモーフィズム。
静的ポリモーフィズムには、仮想関数呼び出しのオーバーヘッドを取り除くことにより、パフォーマンスが向上するという利点もあります。
ベースクラスをテンプレート化したくない場合、またはベースクラスの異なる派生インスタンスを同じ場所(配列やベクトルなど)に保持する必要がある場合は、中間クラスでCRTPを使用して多態的な動作を移動できますそのクラスへ(Wikipediaの多態性コピー構築の例に類似):
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());
-
更新:同様の、より詳細な answer が見つかりました。上記の派生クラスから派生します(例: class AdditionalDerived:public Derived {...}
)、 sizeof
は正しく報告されません。彼はこれを克服するために、コードのより複雑なバリアントを提供します。
誰かが適切な特性を実装する代わりにtype_id()を発明したとは信じられません....