[C++] - dynamic_cast介紹及工作原理、typeid、type_info

Table of Contents

1.dynamic_cast工作原理

2.dynamic_cast介紹

3.static_cast與dynamic_cast

4.typeid


1.dynamic_cast工作原理

《深度探索C++對象模型》中有個例子:

class Point
{
public:
	Point(float xval);
	virtual ~Point();

	float x() const;
	static int PointCount();

protected:
	virtual ostream& print(ostream& os) const;

	float _x;
	static int _point_count;
};

這個類的內存佈局:

type_info是C++ Standard所定義的類型描述器,type_info中放置着每個類的類型信息。virtual table的第一個slot存有type_info的地址。

《The C++ Programming Language》中有個例子:

class My_slider: public Ival_slider { // polymor phic base (Ival_slider has virtual functions)
// ...
};

void g(Ival_box* pb, Date* pd)
{
    My_slider* pd1 = dynamic_cast<My_slider*>(pb); // OK
}

那麼上述dynamic_cast的一個典型實現是:

My_slidertype_info會被編譯器產生出來。pb指向的對象的type_info會在運行時通過vptr取得。這兩個type_info會被交給runtime library函數,比較之後告訴我們是否吻合。如果吻合,返回轉換後的My_slider*;否則返回nullptr

 

2.dynamic_cast介紹

在運行時使用類型信息就叫做“run-time type information, RTTI

downcast:從基類向子類轉換;

upcast:從子類向基類轉換;

crosscast:多重繼承下,從一個基類到兄弟類的轉換.BBwindowlval_box轉換。

dynamic_cast來說,upcast就像賦值操作一樣,並沒有多少耗費資源。dynamic_cast是用來解決編譯時無法知道轉換是否正確的場景。dynamic_cast要求操作對象是指向一個多態類型(含有虛函數)的指針或引用,來進行downcastcrosscast

class My_slider: public Ival_slider { // polymor phic base (Ival_slider has virtual functions)
// ...
};

class My_date : public Date { // base not polymorphic (Date has no virtual functions)
// ...
};

void g(Ival_box* pb, Date* pd)
{
    My_slider* pd1 = dynamic_cast<My_slider*>(pb); // OK
    My_date* pd2 = dynamic_cast<My_date*>(pd); // error : Date not polymorphic
}

多重繼承:

class Component
: public virtual Storable { /* ... */ };
class Receiver
: public Component { /* ... */ };
class Transmitter
: public Component { /* ... */ };
class Radio
: public Receiver, public Transmitter { /* ... */ };

這裏,一個Radio對象有兩個Component基類對象。因此,在一個Radio對象內部從Storable到Component的dynamic_cast是模糊的,將會返回0.

void h1(Radio& r)
{
    Storable* ps = &r; // a Radio has a unique Storable
    // ...
    Component* pc = dynamic_cast<Component*>(ps); // pc = 0; a Radio has two Components
    // ...
}

 

3.static_cast與dynamic_cast

dynamic_cast可以將一個多態虛基類轉換成子類或鄰近兄弟類。但是static_cast不能,因爲它不會對它的操作對象進行檢查類型。

void g(Radio& r)
{
    Receiver* prec = &r; // Receiver is an ordinary base of Radio
    Radio* pr = static_cast<Radio*>(prec); // OK, unchecked
    pr = dynamic_cast<Radio*>(prec); // OK, run-time checked
    Storable* ps = &r; // Storable is a virtual base of Radio
    pr = static_cast<Radio*>(ps); //error : cannot cast from virtual base
    pr = dynamic_cast<Radio*>(ps); // OK, run-time checked
}

編譯器無法預知被void*指針指向的內存的信息。因此,對於需要查看對象類型的dynamic_cast而言,它無法對void*進行轉換。而static_cast在這種情況下可以做到。

Radio* f1(void* p)
{
    Storable* ps = static_cast<Storable*>(p); // trust the programmer
    return dynamic_cast<Radio*>(ps);
}

dynamic_cast和static_cast都無法對const指針或私有繼承的指針進行轉換。static_cast或者reinterpret_cast都無法將操作對象轉換成私有基類。

class Users : private set<Person> { /* ... */ };
void f2(Users* pu, const Receiver* pcr)
{
    static_cast<set<Person>*>(pu); // error : access violation
    dynamic_cast<set<Person>*>(pu); // error : access violation
    static_cast<Receiver*>(pcr); //error : can’t cast away const
    dynamic_cast<Receiver*>(pcr); //error : can’t cast away const
    Receiver* pr = const_cast<Receiver*>(pcr); // OK
    // ...
}

 

4.typeid

typeid()返回指針或引用指向的對象的類型信息type_info。如果typeid()的操作數是nullptr,那麼typeid()會拋出std::bad_typeid的異常。

void f(Shape& r, Shape* p)
{
    typeid(r); // type of the object referred to by r
    typeid(*p); // type of the object pointed to by p
    typeid(p); // type of the pointer, that is, Shape* (uncommon, except as a mistake)
}

type_info定義:

class type_info {
    // data
public:
    virtual ˜type_info(); //is polymorphic
    bool operator==(const type_info&) const noexcept; // can be compared
    bool operator!=(const type_info&) const noexcept;
    bool before(const type_info&) const noexcept; // ordering
    size_t hash_code() const noexcept; // for use by unordered_map and the like
    const char* name() const noexcept; // name of type
    type_info(const type_info&) = delete; // prevent copying
    type_info& operator=(const type_info&) = delete; // prevent copying
};

例子:

struct Poly { // polymor phic base class
    virtual void f();
    // ...
};
struct Non_poly { /* ... */ }; // no virtual functions
struct D1 : Poly { /* ... */ };
struct D2 : Non_poly { /* ... */ };

void f(Non_poly& npr, Poly& pr)
{
    cout << typeid(npr).name() << '\n'; // writes something like "Non_poly"
    cout << typeid(pr).name() << '\n'; // name of Poly or a class derived from Poly
}
void g()
{
    D1 d1;
    D2 d2;
    f(d2,d1); // writes "Non_poly D1"
}

 

參考資料:

1.《深度探索C++對象模型》

2.《The C++ Programming Language》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章