1. 程式人生 > >C++的型別轉換與異常處理

C++的型別轉換與異常處理

一 名稱和語法
1 C語言風格:不管什麼型別的轉換統統是:
TYPE b = (TYPE)a;
2 C++風格:
1)static_cast,靜態型別轉換。如int轉換成char
2)reinterpreter_cast,重新解釋型別
3) dynamic_cast,命名上理解是動態型別轉換。如子類和父類之間的多型型別轉換,執行時進行型別識別,把父類轉換成子類。
4)const_cast,字面上理解就是去const屬性

void main01()
{
double dpi = 3.1415926;
int num1 = (int)dpi; //C型別轉換
int num2 = static_cast<int>(dpi); //靜態型別轉換  編譯的時c++編譯器會做型別檢查
int num3 = dpi; //c語言中 隱式型別轉換的地方 均可使用 static_cast<>() 進行型別轉換

char *p1 = "hello...itcast ";
int *p2 = NULL;
//p2 = static_cast<int*>(p1); // 使用static_cast, 編譯器編譯時,會做型別檢查 若有錯誤 提示錯誤

p2 = reinterpret_cast<int *>(p1); //若不同型別之間,進行強制型別轉換,用reinterpret_cast<>() 進行重新解釋

cout << "p1:" << p1 << endl; //%s指標指向的記憶體空間
cout <<"p2" << p2 << endl; //%d  指標首地址

//總結:通過 reinterpret_cast<>() 和 static_cast<>()把C語言的強制型別轉換 都覆蓋了..
cout<<"hello..."<<endl;
system("pause");
return ;

class Animal
{
public:
virtual void cry() = 0;
};

class Dog : public Animal
{
public:
virtual void cry()
{
    cout << "汪汪" << endl;
}
void doHome()
{
    cout << "看家" << endl;
}
};

class Cat : public Animal
{
public:
virtual void cry()
{
    cout << "喵喵" << endl;
}
void doThing()
{
    cout << "抓老鼠" << endl;
}
};

void playObj(Animal *base)
{
base->cry(); // 1有繼承 2虛擬函式重寫 3 父類指標 指向子類物件  ==>多型
 //需求:在這裡能識別子類物件
// dynamic_cast 執行時型別識別  RIIT

Dog *pDog = dynamic_cast<Dog *>(base);
if (pDog != NULL)//轉換失敗返回NULL
{
    pDog->doHome(); //讓夠 做自己 特有的工作 
}

Cat *pCat = dynamic_cast<Cat *>(base);  //父類物件 ===> 子類物件 
                                        //向下轉型  
                                        //把老子 轉成 小子 
if (pCat != NULL)
{
    pCat->doThing();  //讓夠 做自己 特有的工作 
}
}

void main()
{
Dog d1;
Cat c1;
Animal *pBase = NULL;
pBase = &d1;

playObj(&d1);
playObj(&c1);
system("pause");
}

#include <iostream>
using namespace std;

//const char *p 的const修飾 讓p指向的記憶體空間 變成只讀屬性
void printBuf(const char *  p)
{
p1 = const_cast<char *>(p);//由const char * 轉換成char*
p1[0] = 'Z' ;  //通過p1 去修改了記憶體空間
cout << p << endl;
}

void main()
{
char buf[] = "aaaaaaaaafffffddd";
char *myp = "aaaaaaaaafffffddd";//此時*myp指向的記憶體空間時常量,不可修改。
 //程式設計師 要確保 p所指向的記憶體空間 確實能修改 ;如果不能修改會帶來災難性後果
//printBuf (buf);
printBuf (myp);
system("pause");
}

二 異常處理
1 基本思想
1) C++的異常處理機制使得異常的引發和異常的處理不必在同一個函式中
2)異常是專門針對抽象程式設計中的一系列錯誤處理的
3)異常超脫於函式機制,決定了其對函式的跨越式回跳。
4)異常跨越函式

2 基本語法
1)思想
a)若有異常則通過throw操作建立一個異常物件並拋擲。
b)將可能丟擲異常的程式段嵌在try塊之中。控制通過正常的順序執行到達try語句,然後執行try塊內的保護段。
c)如果在保護段執行期間沒有引起異常,那麼跟在try塊後的catch子句就不執行。程式從try塊後跟隨的最後一個catch子句後面的語句繼續執行下去。
d)catch子句按其在try塊後出現的順序被檢查。匹配的catch子句將捕獲並處理異常(或繼續拋擲異常)。
e)如果匹配的處理器未找到,則執行函式terminate將被自動呼叫,其預設功能是呼叫abort終止程式。(程式down)
f)處理不了的異常,可以在catch的最後一個分支,使用throw語法,向上扔。
g)異常機制與函式機制互不干涉,但捕捉的方式是基於型別匹配。
捕捉相當於函式返回型別的匹配,而不是函式引數的匹配,所以捕捉不用考慮一個拋擲中的多種資料型別匹配問題
h)異常捕捉嚴格按照型別匹配

int divide(int x, int y ) //拋異常
{ 
     if (y ==0) 
     { 
               throw x; //如果執行了丟擲,則後面的程式碼不再執行
     } 
     return x/y; 
} 
void main() //接異常
{ 
     try 
     { 
               cout << "8/2 = " << divide(8, 2) << endl;  ///呼叫
               cout << "10/0 =" << divide(10, 0) << endl; 
     } 
     catch (int e) //捕捉到上面的異常,同一型別
     { 
               cout << "e" << " is divided by zero!" << endl; 
     } 
     catch(...) 
     { 
               cout << "其他未知異常" << endl; 
     } 
     cout << "ok" << endl; 
     system("pause"); 
     return ; 
}

三 棧解旋(unwinding)
1 定義:異常被丟擲後,從進入try塊起,到異常被拋擲前,這期間在棧上的構造的所有物件,都會被自動析構。析構的順序與構造的順序相反。這一過程稱為棧的解旋(unwinding)。

#include <iostream>
using namespace std;
class Test3
{
public:
Test3(int a=0, int b=0)
{
    this->a = a;
    this->b = b;
    cout << "建構函式do \n";
}
~Test3()
{
    cout << "解構函式do \n";
}
private:
int a;
int b;
};

void myDivide() throw (int, char, char *)
{
Test3 t1(1, 2), t2(3, 4);
cout << "myDivide ...要發生異常\n" ;

throw 1;
}

void main()
{
try
{
    myDivide();
}
catch (int a)
{
    cout << "int型別 異常\n" ;
}

catch (...)
{
    cout << " 未知 異常\n" ;
}
cout<<"hello..."<<endl;
system("pause");
return ;
}

四異常介面宣告
1 為了加強程式的可讀性,可以在函式宣告中列出可能丟擲的所有異常型別,例如: void func() throw (A, B, C , D); //函式func()能夠且只能丟擲型別A B C D及其子型別的異常。
2 如果在函式宣告中沒有包含異常介面宣告,則此函式可以拋擲任何型別的異常,例如:void func();
3 一個不拋擲任何型別異常的函式可以宣告為:
void func() throw();
4 如果一個函式丟擲了它的異常介面宣告所不允許丟擲的異常,unexpected函式會被呼叫,該函式預設行為呼叫terminate函式中止程式


五 異常型別及生命週期
1 接受元素時使用異常變數,則進行拷貝構造
2 使用引用的話,catch的是throw的那個物件
3 指標和引用/元素可同時出現,但引用和元素不能同時現如果是指標必須throw的是一個地址(new 申請記憶體)

結論:用引用是最好的


六 標準程式庫異常

C++標準提供了一組標準異常類,這些類以基類exception開始,
標準程式庫丟擲的所有異常,都派生於該基類。

1 案例

// out_of_range
#include "iostream"
using namespace std;
#include <stdexcept> 
#include "string"

// out_of_range
class Teacher
{
public:
Teacher(int age)
{
    if (age > 100)
    {
        string  s = "年齡太大";
        throw out_of_range(s);
    }
    this->age = age;
}
protected:
private:
int age;
};

void main61()
{
try
{
    Teacher t1(102);
}
catch (out_of_range e)
{

    cout << e.what() << endl;
}

exception e;
system("pause");
}

class MyException : public exception
{
public:
MyException(const char *p)
{
    this->m_p = p;
}

virtual const char *  what()
{
    cout << "MyException: 型別" << m_p << endl;
    return m_p;
}
protected:
private:
const char *m_p;
};

void testMyExcept()
{
throw MyException("函式異常");
}
void main()
{

try
{
    testMyExcept();
}
catch (MyException & e)
{
    e.what();
}
catch (...)
{
    cout << "未知 型別 " << endl;
}

system("pause");
}