(十五)C++學習 | 強制型別轉換 異常處理
文章目錄
1. 強制型別轉換
在
C
{\rm C}
C++中存在四種形式的強制型別轉換:static_cast
,interpret_cast
,const_cast
和dynamic_cast
。
1.1 static_cast
用來進行比較自然和低風險的轉換,比如整型和實數型、字元型之間相互轉換等。static_cast
不能在不同型別的指標之間相互轉換,也不能用於整型和指標之間的相互轉換,也不能用於不同型別的引用之間的相互轉換。示例程式:
#include<iostream>
using namespace std;
class A {
public:
// 過載int(),並返回1
operator int() {
return 1;
}
// 過載char*(),並返回NULL
operator char* () {
return NULL;
}
};
int main() {
A a;
int n; char* p = (char*)"New Good.";
// 將3.14強制轉換為3
n = static_cast<int>(3.14);
// 將a物件轉化成int,呼叫int(),n的值變為1
n = static_cast<int>(a);
// p的值變成NULL
p = static_cast<char*>(a);
// 編譯錯誤,static_cast不能將指標轉換成整型
n = static_cast<int>(p);
// 編譯錯誤,static_cast不能將整型轉換成指標
p = static_cast<char*>(n);
return 0;
}
1.2 reinterpret_cast
用來進行各種不同型別的指標之間的轉換,不同型別的引用之間的轉換,指標和能容納得下指標的整數型別之間的轉換。轉換的時候,執行的是逐個位元拷貝的操作。示例程式:
#include<iostream>
using namespace std;
class A {
public:
int i; int j;
A(int n):i(n),j(n){} // 初始化列表
};
int main() {
A a(100);
// 強行讓r引用a
int& r = reinterpret_cast<int&>(a);
// r引用的內容變成200,r引用a前四個位元組,即i的值
r = 200;
// 200 100
cout << a.i << " " << a.j;
int n = 300;
// 強行讓pa指向n
A* pa = reinterpret_cast<A*>(&n);
// n變成400
pa->i = 400;
// 語句不安全,可能導致程式崩潰
pa->j = 500;
long long la = 0x12345678abcdLL;
// la太長,只取低32位0x5678abcd拷貝給pa
pa = reinterpret_cast<A*>(la);
// pa逐個位元拷貝到u
unsigned int u = reinterpret_cast<unsigned int>(pa);
// 5678abcd
cout << hex << u;
typedef void(*PF1)(int);
typedef int(*PF2)(int, char*);
PF1 pf1; PF2 pf2;
// 兩個不同型別的函式指標之間的轉換
pf2 = reinterpret_cast<PF2>(pf1);
return 0;
}
1.3 const_cast
用來去除const
屬性,將常引用轉換成同型別的非常引用,將常指標轉換成同型別的非常指標。例如:
const string s = "Inception";
// 將s轉換成非常引用
string& p = const_cast<string&>(s);
// 將s轉換成非常指標
string* ps = const_cast<string*>(&s);
1.4 dynamic_cast
專門用於將多型基類(包含虛擬函式的基類)的指標或引用,強制轉換為派生類的指標或引用,而且能夠檢查型別轉換的安全性。對於不安全的轉換,轉換結果會返回NULL
。dynamic_cast
不能用於將非多型基類的指標或引用,強制轉換為派生類的指標或引用。示例程式:
#include<string>
#include<iostream>
using namespace std;
class Base {
public:
// 虛解構函式
virtual ~Base() {}
};
// Derived由Base派生而來
class Derived:public Base {};
int main() {
Base b;
Derived d;
Derived* pd;
// 將基類指標b轉化成派生類指標pd
pd = reinterpret_cast<Derived*>(&b);
// reinterpret_cast不檢查安全性,不執行下列語句
if (pd == NULL) {
cout << "unsafe1";
}
// 將基類指標b轉化成派生類指標pd,不安全
pd = dynamic_cast<Derived*>(&b);
// unsafe2
if (pd == NULL) {
cout << "unsafe2";
}
// 將基類指標pb(指向派生類物件)轉化成派生類指標pd,安全
Base* pb = &d;
pd = dynamic_cast<Derived*>(pb);
if (pd == NULL) {
cout << "unsafe2";
}
return 0;
}
2. 異常處理
程式執行過程中難免會發生各種各樣的錯誤,如陣列元素的下標越界、空指標、除數為零等,而造成這些錯誤的原因主要有程式碼質量不高存在 B U G {\rm BUG} BUG、輸入資料不符合程式要求等。而我們總是希望在發生異常情況時,不只是簡單地終止程式的執行,而是能夠反饋異常情況的資訊,並且能夠對程式執行中已發生的事故進行處理。
在
C
{\rm C}
C++中,解決異常的通用做法有以下幾種:在預計會發生異常的地方,加入相應的提示程式碼,但這種程式碼通常不是適用的;把異常與函式介面分開,並且能夠區分不同的異常,並且在函式體外捕獲所發生的異常,並提供更多的異常資訊。對於後者,
C
{\rm C}
C++提供了try、catch
語句用於處理異常。
2.1 try、catch處理異常
示例程式:
#include<iostream>
using namespace std;
int main() {
double m, n;
cin >> m >> n;
// 將可能出現異常的語句放入try內
try {
cout << "before dividing." << endl;
// 除零錯誤,丟擲異常
if (n == 0) {
throw - 1;
}
else
{
cout << m / n << endl;
}
cout << "after dividing." << endl;
}
// 沒有丟擲異常時不會執行catch塊語句,否則根據try塊中的異常值進入catch分支
// 如上述丟擲異常資訊為-1,則進入catch (int e){ };塊中
catch (double d) {
cout << "catch(double)" << d << endl;
}
catch (int e) {
cout << "catch(int)" << e << endl;
}
cout << "finished." << endl;
return 0;
}
下列語句可以捕獲任何形式的異常:
catch (...) {
cout << catch(...) << endl;
}
2.2 異常再丟擲
如果一個函式在執行過程中,丟擲的異常在本函式內就被捕獲並處理了,那麼該異常就不會拋給這個函式的呼叫者(山一層函式);如果異常在本函式內沒有被處理,則該異常就會繼續被拋給上一層函式。示例程式:
#include<string>
#include<iostream>
using namespace std;
class CException {
public:
string msg;
CException(string s) :msg(s) {} // 初始化列表
};
double Devide(double x, double y) {
// 丟擲異常為CException類物件
if (y == 0) {
throw CException("divided by zero");
}
cout << "in Devide" << endl;
return 0;
}
int CountTax(int salary) {
try {
// 如果salary小於零則丟擲整型異常
if (salary < 0) {
throw - 1;
}
cout << "counting tax" << endl;
}
catch (int) {
cout << "salary 0" << endl;
}
cout << "tex counted." << endl;
return salary * 0.15;
}
int main() {
double f = 1.2;
try {
CountTax(-1); // 引數小於零會引發異常,輸出salary 0
f = Devide(3, 0); // 除數為零會引發異常
cout << "end of try block." << endl;
}
// CException型別異常,輸出divided by zero
catch (CException e) {
cout << e.msg << endl;
}
cout << "f=" << f << endl;
cout << "finished." << endl;
return 0;
}
3. STL中的異常類
標準模板庫中有關異常的類都繼承自基類exception
,有bad_typeid
、bad_cast
、bad_alloc
、ios_bas::failure
和logic_error
等。
3.1 bad_cast
在使用dynamic_cast
進行從多型基類物件或引用到派生類的引用的強制型別轉換,如果轉換是不安全的,則會丟擲此異常。示例程式:
#include<typeinfo>
#include<iostream>
#include<stdexcept>
using namespace std;
// 有虛成員函式,是多型基類
class Base {
virtual void func() {}
};
// Derived繼承自Base
class Derived :public Base {
public:
void Print() {}
};
void PrintObj(Base& b) {
try {
// 強制型別轉換,將b轉換成Derived&,即基類引用->派生類引用
Derived& rd = dynamic_cast<Derived&>(b);
// 若轉換不安全,會丟擲bad_cast異常
rd.Print();
}
// 如果b引用的是基類物件,則轉換是不安全的,輸出Bad dynamic_cast
catch (bad_cast& e) {
cerr << e.what() << endl;
}
}
int main() {
Base b;
PrintObj(b);
return 0;
}
3.2 bad_alloc
在使用new
關鍵字進行動態記憶體分配時,如果沒有足夠的記憶體供使用,則會引發此異常。示例程式:
#include<iostream>
#include<stdexcept>
using namespace std;
int main() {
try {
// 無法分配這麼多空間,丟擲異常
char* p = new char[0x7fffffff];
}
// 輸出bad allocation
catch (bad_alloc& e) {
cerr << e.what() << endl;
}
return 0;
}
3.3 out_of_range
用vector
、string
的at
成員函式根據下標訪問元素時,如果下標越界,則會丟擲此異常。示例程式:
#include<vector>
#include<string>
#include<iostream>
#include<stdexcept>
using namespace std;
int main() {
vector<int> v(10);
try {
// 丟擲out_of_range異常
v.at(100) = 100;
}
// 捕獲異常,輸出invalid vector subscript
catch (out_of_range& e) {
cerr << e.what() << endl;
}
string s = "hello";
try {
// 丟擲out_of_range異常
char c = s.at(100);
}
// 捕獲異常,輸出invalid string position
catch (out_of_range& e) {
cerr << e.what() << endl;
}
return 0;
}
4. 總結
在
C
{\rm C}
C++中,我們可以簡單地使用int(...)
等形式進行強制型別轉換,但這種方式不檢查轉換的安全性,本文介紹的幾種強制型別轉換方式適用於不同的場景。在不同場景使用不同的強制型別轉換方式,可以有效地提高程式的可讀性。對於異常處理,主要依靠try、catch
塊處理,在編寫大型程式時,對於利用好異常處理非常重要。因此健壯的程式總是能夠自行發現錯誤,並給出修改意見,而異常處理模組在其中發揮著舉足輕重的重要。
參考
- 北京大學公開課:程式設計與演算法(三)C++物件導向程式設計.
相關文章
- C++強制型別轉換C++型別
- C C++ 強制型別轉換C++型別
- C++異常處理機制C++
- 強制型別轉換型別
- C++ 常型別轉換C++型別
- 強制型別轉換之(==)型別
- C++ 異常處理機制詳解:輕鬆掌握異常處理技巧C++
- 造型與強制型別轉換型別
- Python入門學習之異常處理機制Python
- 異常處理機制
- [轉載] Java異常處理習題Java
- JAVA學習之異常處理Java
- 異常處理機制(二)之異常處理與捕獲
- JS在if中的強制型別轉換JS型別
- 深入淺出說強制型別轉換型別
- Java異常處理機制Java
- rust學習十、異常處理(錯誤處理)Rust
- 異常處理 - Go 學習記錄Go
- 【C++】 C++異常捕捉和處理C++
- 《C++ Primer》學習筆記(五):迴圈、分支、跳轉和異常處理語句C++筆記
- Javascript基礎之-強制型別轉換(三)JavaScript型別
- Javascript基礎之-強制型別轉換(一)JavaScript型別
- JavaScript強制型別轉換的背後操作JavaScript型別
- javascript強制型別轉換與操作符JavaScript型別
- c++ 型別轉換C++型別
- Kotlin DSL C++專案引入OpenCV異常處理(轉)KotlinC++OpenCV
- Java 的異常處理機制Java
- 8.異常處理機制
- 08.異常處理機制
- SpringMVC異常的處理機制SpringMVC
- java異常的處理機制Java
- swoft 學習筆記之異常處理筆記
- Python學習之 異常處理詳解Python
- SpringMVC學習系列(10) 之 異常處理SpringMVC
- C++錯誤和異常處理C++
- C++整理19_異常處理C++
- 【C++】C++之型別轉換C++型別
- 強制型別轉換(int)、(int&)和(int*)的區別型別