- ランタイムタイプ識別(RTTI)関数は、二つのオペレータによって実現されます。
- typeidの演算子:式の戻り値の型
- dynamic_cast演算子:基本クラスへのポインタまたは参照を安全に派生クラスへのポインタまたは参照に変換しました
- 我々両方のポインタまたは参照の特定の種類の演算子、およびクラスが仮想関数が含まれている場合、オペレータは、ポインタを使用するか、または参照オブジェクトがバインド動的タイプ
- 次のような場合のために、これらの演算子:
- 我々は、基本クラスのオブジェクトへのポインタまたは参照を使用する実行派生クラスの操作を、操作は、仮想関数ではありません
- 一般的には可能な限り、我々は仮想関数を使用しようとする必要があり、話します。動作は、仮想関数として定義されている場合、コンパイラが自動的にオブジェクトの動的な型に基づいて関数の正しいバージョンを選択します。しかし、仮想関数を定義することなく、すべての時間は、我々は仮想関数を使用することができないと仮定して、あなたはRTTI演算子を使用することができます
- 一方、仮想関数に比べ、使用RTTIオペレータはより多くの潜在的なリスクが含まれています。プログラマは変換の特定のタイプをクリアする必要があり、変換のタイプが正常に実行されているかどうかをチェックしなければなりません
A、dynamic_cast演算子
- 次のようにdynamic_castをオペレータの使用形態は、次のとおりです。
- タイプ:クラス型でなければならない、とタイプは、通常、仮想関数が含まれている必要があります
- E:
- 最初の形式では:E有効なポインタでなければなりません
- 2番目の形式では:eは左辺値でなければなりません
- 三番目の形式では:eは、左値をすることはできません
- 上記のすべての形態では、電子の種類は、次の3つの条件のいずれかを満たしている必要があります。
- Eは、ターゲット型の公共派生クラス型であります
- Eは、ターゲット型のパブリック基本クラスタイプです
- E型のターゲット型のタイプです
- 戻り値エラーが発生します。
- 声明はdynamic_cast変換目標がある場合はポインタ型と失敗し、結果は0
- 声明はdynamic_castコンバージョン目標がある場合は参照型と失敗し、その後、オペレータはdynamic_cast 異常bad_castをスローします
dynamic_castをのポインタ型
- 仮定Baseが仮想関数を含む、および誘導基は、公共の派生クラスであります
- ポインタベースポインタBPがあれば派生し、その後、我々は、実行時に指し示すポインタに変換することができます。コードは以下の通りであります:
class Base {
public:
virtual void foo() {} //必须含有虚函数,否则不能执行dynamic_cast
};
class Derived :public Base {};
int main()
{
Base *bp = new Base;
//成功返回Derived指针,失败返回0
if (Derived *dp = dynamic_cast<Derived*>(bp))
{
//使用dp指向的Derived对象
}
else
{
//使用bp指向的Base对象
}
return 0;
}
- 成功した点BP派生オブジェクト場合、上述した型変換の初期化点はDPとせ派生オブジェクトは、BP呼びます。この場合には:
- 文は内部派生オペレーションコードを使用する場合は安全です
- 文の条件は、その後、適切な基本操作にelse節のポインティングを失敗した場合、それ以外の場合は、0に型変換の結果は、0手段をDP
- それは、我々は、部分的には、条件DPを定義する、ということに注目すべきである、そうすることの利点は次のとおりです。
- 同時に、完全な変換の種類や状態のチェック1回の操作で二つのタスク
- また、if文の外ポインタDPはアクセスできません。変換は、後続の決意はそれに応じてコードを忘れてしまった場合でも、失敗したら、セキュリティプログラムに結合していないポインタに接触しません
dynamic_castをの参照タイプ
- 同様の参照型とポインタ型のdynamic_castのdynamic_castを使用することは、ときに、2つの異なるエラー戻り値を除きます。
- エラーが発生した場合にはdynamic_castの参照型は、例外がスローされます
- ポインタ型のdynamic_castは0を返したときにエラー
- 型変換参照が失敗した場合、プログラムは、STDをスロー:: bad_castが異常と呼ばれ、例外が定義されているヘッダファイルのTypeInfoに
- 例えば:
class Base {
public:
virtual void foo() {} //必须含有虚函数,否则不能执行dynamic_cast
};
class Derived :public Base {};
void f(const Base &b)
{
try {
//如果出错,将抛出bad_cast异常
const Derived &d = dynamic_cast<const Derived&>(b);
}
catch (bad_cast) {
}
}
二、typeidのオペレータ
- 第RTTIオペレータを提供するためにはタイプIDオペレータ、ある表現のオブジェクト・タイプが属する返します
- タイプIDオペレータフォーマット型ID(E) Eは式または名前の任意のタイプとすることができます、
- タイプID値が一定の基準オブジェクトで返し、オブジェクトのタイプは、標準ライブラリ派生型またはパブリックTYPE_INFO TYPE_INFOタイプであります
- typeid演算子のいくつかの注意事項:
- タイプIDのオペレータは、式の任意のタイプに適用することができます。いつものように、トップのconstは無視されます
- 式が参照である場合、タイプIDの戻り型は、参照オブジェクトを引用し
- タイプIDスコープ配列、または関数場合、標準型のポインタへの変換を行いません。すなわち、我々は配列Aの型ID()を実行した場合、得られた結果は、配列型の代わりに、ポインタ型であります
- 分析の動的タイプと静的タイプ(重要):
- オペランドは、任意のクラスの仮想関数が含まれていないタイプのいずれかの一部ではない場合、タイプIDのオペレータを示し、オペランドの静的タイプ
- 左のオペランドが少なくともクラス、仮想関数の値で定義する場合、タイプIDの結果は実行時まで得られません
オペレータはタイプIDを使用します
- 通常は、我々はTYPEID使う2つの式を比較し、同じ種類ある、またはタイプの式は指定された型と同じである比較
- ケースプレゼンテーション:
class Base {
public:
virtual void foo() {}
};
class Derived :public Base {};
int main()
{
Derived *dp = new Derived;
Base *bp = dp;
//运行时比较,如果bp与dp指向于同一类型的对象才执行if
if (typeid(*bp) == typeid(*dp)) {
}
//运行时比较,当bp指向于Derived类型对象才执行
if (typeid(*bp) == typeid(Derived)) {
}
return 0;
}
- どのようになるベースクラスの仮想関数を設計します:
- typeidの操作は実行時のポインタ(分析の実行)で分析され、ため、クラスは仮想関数が含まれていた後、
- クラスが仮想関数が含まれていない場合、ポインタダウンのタイプは、ベースは、仮想関数が含まれていない場合ので、その後、上記の二つの場合にfalseを返す、(コンパイル時に解決)コンパイル時に決定されます
- 注:typeidのアプリケーションでは、オブジェクトのポインタではありません次のコードはfalseを返しますもしそうなら、:
Derived *dp = new Derived;
Base *bp = dp;
//下面的if将返回false,因为bp的类型是指向Base的指针,dp是指向Derived的指针
if (typeid(bp) == typeid(dp)) {
std::cout << "1" << std::endl;
}
//下面的if将返回false,因为bp的类型是指向Base的指针
if (typeid(bp) == typeid(Derived)) {
std::cout << "2" << std::endl;
}
- オブジェクト・タイプがクラスの仮想関数を含むポインタであるが、ポインタ自体は、クラスタイプのオブジェクトではありません。タイプベース*はコンパイル時に評価され、それは明らかに異なっており、派生します
- ランタイムは式が評価されるかどうかを決定するかどうかを確認する必要がTYPEID:
- タイプ場合のみ、仮想関数を含むが、コンパイラは式を評価します
- 逆に、型は仮想関数が含まれていない場合、表現を返しますの静的型は型ID;コンパイラは、式の評価は式の静的タイプのものとすることができる知ることができません
- 式の動的タイプは、静的型と異なるかもしれ場合、それは実行時に戻り値の種類を決定するために式を評価しなければなりません。この規則は、型ID(* p)の状況に適用されます。
- ポインタpが含まれていないタイプは、仮想関数を参照する場合は、pが有効なポインタである必要はありません。
- それ以外の場合は、実行時に評価P *は、その後、pは有効なポインタでなければなりません
- pがNULLポインタである場合には、(P *)TYPEID bad_typeidと呼ばれる例外がスローされます
第三に、使用RTTI
実際にはRTTIアプリケーション
- RTTIは、いくつかのケースで有用です:
- 例えば、私たちが望む等価演算子を実装するクラスの継承を楽しむ時間を
- 場合は、これら二つのオブジェクトのために、彼らは同じ型と対応するデータメンバの同じ価値がある、我々は2つのオブジェクトが等しいことを言います
- クラスの継承階層では、各派生クラスでは、独自のデータメンバーを追加するための責任があるので、派生クラスの等価演算子は、派生クラスの新しいメンバーである必要があります考慮されています
- 簡単な解決策を考える仮想関数の集合を定義することです。
- 作成し、それぞれ、すべてのレベルで同等の継承システムの実装にその判断を。この場合、我々は、オペレータが同等の実際の動作を担当するその仮想関数(同じ名前を仮定)を、委任する動作する、ベース・クラスは、参照等価演算子で定義することができます。
- 仕事にしかし、これらの解決策は難しいです。基本クラスのバージョンと仮想派生クラスのバージョンでは、同じパラメータタイプを持っている必要があります。我々は同等の仮想関数を定義したい場合は、関数のパラメータは、基本クラスへの参照でなければなりません。この場合、等しいのみ基底クラス使用のメンバーの関数であり、派生クラスの一意のメンバーを比較することはできません
- 真に効果的な等価比較演算をしたい、我々はRTTIメカニズムを使用することができます。
- 我々は形状を定義し、検査の使用のタイプは2つのオペランドが同じ型IDたときにオペレータを均等化するために、ベースクラスへの参照
- オペランドの場合は矛盾の種類、 == falseを返します。機能の同じタイプのみ等しいと呼ばれ、同等比較型機能の各クラス定義は、独自のメンバーに責任があります
- 例えば:==演算子は、比較動作を行う、ベース・パラメータを受け入れるが、オペレータへの変換動作の第1のクラスタイプの前にオブジェクトに属し
ケース・プレゼンテーション
- より良い上記の概念を説明するために、我々は、次の2つのカテゴリーを定義します。
class Base {
public:
friend bool operator==(const Base&, const Base&);
public:
//类的接口成员
protected:
virtual bool equal(const Base&)const;
};
class Derived :public Base {
public:
//类的接口成员
protected:
bool equal(const Base&)const;
};
- 次に、我々は全体的な等価演算子を定義する方法をご紹介します:
bool operator==(const Base& lhs, const Base& rhs)
{
//如果typeid不相同返回false,&&后面的虚函数就不调用了
//如果typeid返回true,继续执行equal虚函数
return (typeid(lhs) == typeid(rhs)) && (lhs.equal(rhs));
}
- 上記のコード:
- オペランドリターンの異なるタイプがfalseの場合、仮想関数呼び出しのバックではありません
- 操作対象の同じ戻り値の型がtrueの場合、同等の仮想関数呼び出しの後ろに続けます
- ベースを呼び出すときに、オペランドは、ベースオブジェクトのとき::等しいです
- ときにオブジェクト派生オペランドは、コールが派生::等しいです
- 派生クラスの仮想関数をEQUAL:
- 各クラスの継承階層は、独自の同等の機能を定義する必要があります。行うには、派生クラス最初のすべての機能は、つまり、同じ派生クラス型の引数の型
- 次のように導出さ等しい仮想関数は、例えば派生クラスを定義しました:
bool Derived::equal(const Base& rhs)const
{
auto r = dynamic_cast<const Derived&>(rhs);
//执行比较两个Derived对象的操作并返回结果
}
- 等しい基底クラスの仮想関数:
- これは、基底クラスの仮想関数は比較的簡単である定義します
- *これは、ベースとパラメータオブジェクトであるため、現在のオブジェクトへの直接比較、無型変換
bool Base::equal(const Base& rhs)const
{
//直接比较Base对象的操作
}
四、クラスTYPE_INFO
- 正確に定義されたクラスをTYPE_INFO 変わる異なるコンパイラ、と。しかし、C ++標準TYPE_INFOがなければならないヘッダファイルのTypeInfoに定義します
- TYPE_INFOは、次の操作を定義します。
- TYPE_INFOクラスは通常、基本クラスとして発生するので、そうそれは仮想デストラクタ公共を提供する必要があります。望ましいコンパイラた場合の情報の追加の種類を提供するためには、通常、派生クラスで行わTYPE_INFO
- クラスの機能TYPE_INFO:
- TYPE_INFO なしデフォルトコンストラクタ
- TYPE_INFO コピーや移動コンストラクタ、代入演算子や削除のように定義されています。したがって、我々は、定義またはコピーすることはできませんタイプTYPE_INFOオブジェクトを、ほかの型のオブジェクトTYPE_INFOに割り当てることはできません
- TYPE_INFOオブジェクトを作成するための唯一の方法は、型ID演算子を使用することです
名前()メンバ関数
- TYPE_INFOクラスのメンバ関数名は、オブジェクト型の名前を表すCスタイルの文字列を返します。
- いくつかのタイプでは、与えられた、名前の値が返されたコンパイラ固有により、必ずしもプログラムで使用される名前と一致していません
- ケースプレゼンテーション:
#include <iostream>
#include <string>
#include <vector>
#include <new>
using namespace std;
class Base {
public:
virtual void foo() {}
};
class Derived :public Base {};
int main()
{
int arr[10];
Derived d;
Base *p = &d;
std::cout << "42: " << typeid(42).name() << std::endl;
std::cout << "arr: " << typeid(arr).name() << std::endl;
std::cout << "string: " << typeid(std::string).name() << std::endl;
std::cout << "d: " << typeid(d).name() << std::endl;
std::cout << "p: " << typeid(p).name() << std::endl;
std::cout << "*p: " << typeid(*p).name() << std::endl;
std::cout << "Base: " << typeid(Base).name() << std::endl;
std::cout << "Derived: " << typeid(Derived).name() << std::endl;
return 0;
}