C++ 多態(3): RTTI 和 異常處理

在這裏插入圖片描述

注:轉載請標明原文出處鏈接:https://xiongyiming.blog.csdn.net/article/details/100729020


1 RTTI

RTTI (Run-Time Type Identification, 運行時類型識別),通過運行時類型信息程序能夠使用基類的指針或引用來檢查這些指針或引用所指的對象的實際派生類型。

RTTI提供了以下兩個非常有用的操作符:

  1. typeid 操作符,返回指針和引用所指的實際類型。
  2. dynamic_cast 操作符,將基類類型的指針或引用安全地轉換爲派生類型的指針或引用。

下面舉一個例子:

在這裏插入圖片描述

下面兩個類繼承Flyable類

在這裏插入圖片描述


在這裏插入圖片描述


在這裏插入圖片描述

如果對參入的指針能進行判決,就需要使用RTTI, 如下圖所示:

在這裏插入圖片描述

dynamic_cast 注意事項
(1) 只能應用於指針和引用的轉換;
(2) 要求的類型中必須包含虛函數;
(3) 轉換成功返回子類的地址,失敗返回NULL


typeid 注意事項
(1) typeid返回一個type_info對象的引用;
(2) 如果想通過基類的指針獲得派生類的數據類型,基類必須帶有虛函數;
(3) 只能獲取對象的實際類型.



代碼示例

要求

RTTI

  1. Flyable類
    成員函數:takeoff land

  2. Plane類
    成員函數:takeoff land carry

  3. Bird類
    成員函數:takeoff land foraging

  4. 全局函數doSomething(Flyable *obj)


Flyable.h

#pragma once

class Flyable
{
public:
	virtual void takeoff() = 0;//純虛函數
	virtual void land() = 0;//純虛函數

};

Plane.h

#pragma once
#include<iostream>
#include<string>
#include"Flyable.h"

using namespace std;

class Plane :public Flyable
{
public:
	void carry();
	virtual void takeoff();
	virtual void land();
};

Plane.cpp

#include<iostream>
#include<string>
#include"Plane.h"

using namespace std;

void Plane::carry()
{
	cout << "Plane--carry() " << endl;
}

void Plane::takeoff()
{
	cout << "Plane--takeoff() " << endl;
}


void Plane::land()
{
	cout << "Plane--land() " << endl;
}

Bird.h

#pragma once
#include<iostream>
#include<string>
#include"Flyable.h"

using namespace std;

class Bird :public Flyable
{
public:
	void foraging();
	virtual void takeoff();
	virtual void land();
};

Bird.cpp

#include<iostream>
#include<string>
#include"Bird.h"

using namespace std;

void Bird::foraging()
{
	cout << "Bird--foraging() " << endl;
}

void Bird::takeoff()
{
	cout << "Bird--takeoff() " << endl;
}

void Bird::land()
{
	cout << "Bird--land() " << endl;
}

main.cpp

#include<iostream>
#include"Plane.h"
#include"Bird.h"

using namespace std;

/***************
RTTI
1. Flyable類
	成員函數:takeoff  land
	
2. Plane類
	成員函數:takeoff  land  carry

3. Bird類
	成員函數:takeoff  land  foraging
	
4 全局函數doSomething(Flyable *obj)

***************/

void doSomething(Flyable *obj)
{
	cout << typeid(*obj).name() << endl;
	obj->takeoff();
	if (typeid(*obj) == typeid(Bird))
	{
		Bird *bird = dynamic_cast<Bird *>(obj);
		bird->foraging();
	}

	if (typeid(*obj) == typeid(Plane))
	{
		Plane *plane = dynamic_cast<Plane *>(obj);
		plane->carry();
	}
	obj->land();

}

int main(void)
{
	Bird b;
	doSomething(&b);
	cout << "--------------------------------" << endl;

	Plane p;
	doSomething(&p);
	cout << "--------------------------------" << endl;

	int i = 0;
	cout << "typeid(i).name= " << typeid(i).name()<< endl;
	cout << "--------------------------------" << endl;

	double j = 0;
	cout << "typeid(j).name= " << typeid(j).name() << endl;
	cout << "--------------------------------" << endl;


	Flyable *a = new Bird();
	cout << "typeid(a).name= " << typeid(a).name() << endl;
	cout << "typeid(*a).name= " << typeid(*a).name() << endl;

	cin.get();
	return 0;
}

運行結果

在這裏插入圖片描述



2 異常處理

異常:程序運行出現的錯誤。
異常處理:對有可能發生異常的地方做出預見性的安排。
異常是程序在執行期間產生的問題。C++ 異常是指在程序運行時發生的特殊情況,比如嘗試除以零的操作。

常見的異常:數組下標越界,除數爲0,內存不足 等

如果不進行異常處理,程序就會崩潰。那麼如何進行該異常處理?
異常處理的基本思想:主邏輯與異常處理分離。

異常提供了一種轉移程序控制權的方式。

C++ 異常處理涉及到三個關鍵字:try、catch、throw

  1. throw: 當問題出現時,程序會拋出一個異常。這是通過使用 throw 關鍵字來完成的。
  2. catch: 在您想要處理問題的地方,通過異常處理程序捕獲異常。catch 關鍵字用於捕獲異常。
  3. try: try 塊中的代碼標識將被激活的特定異常。它後面通常跟着一個或多個 catch 塊。

C++中異常處理是如何工作的? 如下圖所示:

在這裏插入圖片描述

如果有一個塊拋出一個異常,捕獲異常的方法會使用 try 和 catch 關鍵字。try 塊中放置可能拋出異常的代碼,try 塊中的代碼被稱爲保護代碼。使用 try/catch 語句的語法如下所示:

try
{
   // 保護代碼
}catch( ExceptionName e1 )
{
   // catch 塊
}catch( ExceptionName e2 )
{
   // catch 塊
}catch( ExceptionName eN )
{
   // catch 塊
}

如果 try 塊在不同的情境下會拋出不同的異常,這個時候可以嘗試羅列多個 catch 語句,用於捕獲不同類型的異常。


(1) 拋出異常

可以使用 throw 語句在代碼塊中的任何地方拋出異常。throw 語句的操作數可以是任意的表達式,表達式的結果的類型決定了拋出的異常的類型。

以下是嘗試除以零時拋出異常的實例:

{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}



(2) 捕獲異常

catch 塊跟在 try 塊後面,用於捕獲異常。您可以指定想要捕捉的異常類型,這是由 catch 關鍵字後的括號內的異常聲明決定的。

try
{
   // 保護代碼
}catch( ExceptionName e )
{
  // 處理 ExceptionName 異常的代碼
}

上面的代碼會捕獲一個類型爲 ExceptionName 的異常。如果您想讓 catch 塊能夠處理 try 塊拋出的任何類型的異常,則必須在異常聲明的括號內使用省略號 …,如下所示:

try
{
   // 保護代碼
}catch(...)
{
  // 能處理任何異常的代碼
}



(3) 代碼示例

下面是一個實例,拋出一個除以零的異常,並在 catch 塊中捕獲該異常。

#include <iostream>
using namespace std;
 
double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}
 
int main ()
{
   int x = 50;
   int y = 0;
   double z = 0;
 
   try {
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg) {
     cerr << msg << endl;
   }
 
   return 0;
}

運行結果

由於拋出了一個類型爲 const char* 的異常,因此,當捕獲該異常時,我們必須在 catch 塊中使用 const char*。當上面的代碼被編譯和執行時,它會產生結果如下圖所示。

在這裏插入圖片描述




參考資料

[1] C++遠征之多態篇
[2] https://www.runoob.com/cplusplus/cpp-exceptions-handling.html

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