目錄
- 動態類型轉換
- 1、為何需要動態類型轉換
- 2、dynamic_cast<>();運算符
- 3、向上轉換和向下轉換( Upcasting and Downcasting)
- 4、 基類對象和派生類對象的互操作
- 5、Upcasting/Downcasting與繼承鏈上不同類的對象之間的賦值有什么關系和區別?
- typeid 運行時查詢類型的信息
- RTTI與typeid 的關系
動態類型轉換
1、為何需要動態類型轉換
定義一個函數:
void printObject(Shape& shape)
//shape是派生類對象的引用
{cout<<"The area is"<<shape.getArea()<<endl;//如果是circle,就輸出半徑//如果是rectangle,就輸出寬和高
}
如果這個shape指向的是一個circle類型的對象,我們想顯示它的半徑,該怎么辦?
2、dynamic_cast<>();運算符
dynamic_cast<>();
沿著繼承層次向上、向下以及側向轉換到類的指針和引用。
轉指針:失敗返回nullptr
轉引用:失敗拋異常
步驟:
1、先將shape類對象用dynamic_cast轉換為派生類對象
2、然后調用派生類中獨有的函數
如下所示:
void printObject(Shape& shape)
//shape是派生類對象的引用
{cout<<"The area is"<<shape.getArea()<<endl;//先獲取基類指針Shape *p = &shape;//將基類指針轉換為派生類指針Circle *c = dynamic_cast<Circle*>(p);//Circle& c = dynamic_cast<Circle&>(shape);//引用轉換失敗則拋出一個異常 std::bad_cast//如果shape是Circle對象,那么會成功,如果不是則不會成功if(c != nullptr) //轉換失敗則指針為空{cout<<"The radius is"<<c->getRadius()<<endl;}
}
3、向上轉換和向下轉換( Upcasting and Downcasting)
upcasting:將派生類類型指針賦值給基類類型指針。
downcasting : 將基類類型指針賦值給派生類類型指針。
![]() | ![]() |
Shape* s = nullptr;Circle *c = new Circle(2);s = c; //OK,隱式上轉
2、下轉必須顯式執行
Shape* s = new Circle(1);Circle *c = nullptr;c = dynamic_cast <Circle*> (s); //顯式下轉
什么是側向轉換?
動態類型轉換可以上轉也可以下轉。那么什么是側向轉換?
比如說circle和rectangle都是繼承的shape,他們同級之間相互轉換就是側向轉換。
4、 基類對象和派生類對象的互操作
之前的向上和向下轉換都是以指針為例,現在不是指針了,而是對象。
首先看對象的內存布局:
首先定義一個父類和一個子類對象:
Shape S;
Circle C;
對象成員一覽:
1、可以看出來派生類對象比基類對象多出來一點東西。
2、派生類里面包含了一個基類對象的數據
同時給出兩個規則:
1、可將派生類對象截斷,只使用繼承來的信息
2、但不能將基類對象加長,無中生有變出派生類對象
了解到上面的知識之后,判斷下面四個對錯:
1、S = C;
2、C = S;
3、Shape &rS = C;
4、Circle &rC = S;
顯然是13對(將派生類對象截斷,只使用繼承來的信息),24錯(不能使基類對象加長,無中生有變出派生類對象)
5、Upcasting/Downcasting與繼承鏈上不同類的對象之間的賦值有什么關系和區別?
向上向下轉換只要是在同一個繼承鏈上都是可以成立的。
而不同對象之間的賦值,只允許從下往上賦值,傳遞被繼承的信息。
typeid 運行時查詢類型的信息
typeid用于獲取對象所屬的類的信息:
(1) typeid returns a reference to an object of class type_info. (typeid運算符返回一個type_info對象的引用)
(2) typeid(AType).name() 返回實現定義的,含有類型名稱的C風格字符串(char *)
#include <typeinfo> //使用typeid,需要包含此頭文件
#include <iostream>
class A {};
A a{};
// ……
int main()
{auto& t1 = typeid(a);if (typeid(A) == t1) {std::cout << "a has type "<< t1.name() << std::endl;}
}
結果:
RTTI與typeid 的關系
RTTI(Run-Time Type Identification),通過運行時類型信息程序能夠使用基類的指針或引用來檢查這些指針或引用所指的對象的實際派生類型。
RTTI提供了以下兩個非常有用的操作符:
(1)typeid操作符,返回指針和引用所指的實際類型。
(2)dynamic_cast操作符,將基類類型的指針或引用安全地轉換為派生類型的指針或引用。
面向對象的編程語言,像C++,Java,delphi都提供了對RTTI的支持。 本文將簡略介紹 RTTI 的一些背景知識、描述 RTTI 的概念,并通過具體例子和代碼介紹什么時候使用以及如何使用 RTTI;本文還將詳細描述兩個重要的 RTTI 運算符的使用方法,它們是 typeid 和dynamic_cast。
其實,RTTI 在C++中并不是什么新的東西,它早在十多年以前就已經出現了。但是大多數開發人員,包括許多高層次的C++程序員對它并不怎么熟悉,更不用說使用 RTTI 來設計和編寫應用程序了。
一些面向對象專家在傳播自己的設計理念時,大多都主張在設計和開發中明智地使用虛擬成員函數,而不用 RTTI 機制。但是,在很多情況下,虛擬函數無法克服本身的局限。每每涉及到處理異類容器和根基類層次(如 MFC)時,不可避免要對對象類型進行動態判斷,也就是動態類型的偵測。如何確定對象的動態類型呢?答案是使用內建的 RTTI 中的運算符:typeid 和 dynamic_cast。
在C++中存在虛函數,也就存在了多態性,對于多態性的對象,在程序編譯時可能會出現無法確定對象的類型的情況。當類中含有虛函數時,其基類的指針就可以指向任何派生類的對象,這時就有可能不知道基類指針到底指向的是哪個對象的情況,類型的確定要在運行時利用運行時類型標識做出。