抽象類與純虛函數
1. 抽象類
類太抽象以至於無法實例化就叫做抽象類(描述是不是絕了,類越往下派生肯定越具體,越往上肯定越抽象)
2. 抽象函數/純虛函數
2.1. 成員函數應出現在哪個繼承層次?
問題:Shape類層次中,getArea()函數放在哪個層次
選擇1:放哪兒都行:Shape中或子類中定義getArea()
選擇2:強制要求Shape子類必須實現getArea()
2.2. 抽象函數(abstract functions)要求子類實現它
virtual double getArea() = 0; // 在Shape類中
Circle子類必須實現getArea()純虛函數才能實例化
2.3. 包含抽象函數的類被稱爲抽象類
抽象類不能實例化(創建對象)
3. Abstract Class Example
動態類型轉換
1. 爲何需要動態類型轉換
void printObject(Shape& shape)
// shape是派生類對象的引用
{
cout << "The area is "
<< shape.getArea() << endl;
// 如果shape是Circle對象,就輸出半徑
// 如果shape是Rectangle對象,就輸出寬高
}
如果你要修改函數,讓它顯示圓的半徑該怎麼辦?
2. Dynamic Casting Example
2.1. dynamic_cast 運算符
(1) 沿繼承層級向上、向下及側向轉換到類的指針和引用
(2) 轉指針:失敗返回nullptr
(3) 轉引用:失敗拋異常
2.2. 例子
先將Shape對象用dynamic_cast轉換爲派生類Circle對象
然後調用派生類中獨有的函數
// A function for displaying a Shape object
void printObject(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
if (c != nullptr) // 轉換失敗則指針爲空
{
cout << "The radius is "
<< p1->getRadius() << endl;
cout << "The diameter is "
<< p1->getDiameter() << endl;
}
}
向上轉換和向下轉換
1. 向上/向下 轉型
1.1. upcasting : 將派生類類型指針賦值給基類類型指針
1.2. downcasting : 將基類類型指針賦值給派生類類型指針
Upcast |
Downcast |
computer = pc |
pc = computer |
computer = desktop |
desktop = computer |
pc = desktop |
desktop = pc |
2. 向上/向下 轉型
2.1. 上轉可不使用dynamic_cast而隱式轉換
Shape* s = nullptr;
Circle *c = new Circle(2);
s = c; //OK,隱式上轉
2.2. Downcasting must be performed explicitly. (下轉必須顯式執行)
Shape* s = new Circle(1);
Circle *c = nullptr;
c = dynamic_cast <Circle*> (s); //顯式下轉
父上,子下
上轉隱,下轉顯
3. 基類對象和派生類對象的互操作
3.1. 對象內存佈局
Shape S;//基類對象
Circle C;//派生類對象
1可將派生類對象截斷,只使用繼承來的信息(意思是派生對象可以賦值給基類對象,截斷自己將繼承來的部分賦值給基類)
2但不能將基類對象加長,無中生有變出派生類對象(二基類對象不能賦值給派生類對象,因爲radius無從賦值)
那麼,下面四個語句的正確與否很顯而易見了:
①S = C; (√)
②C = S; (×)
③Shape &rS = C;(√)
④Circle &rC = S; (×)
不恰當的記錄下:(保護屬性的數據或函數可被派生類成員訪問)