1. 程式人生 > >程式設計與演算法(三)第十週 c++新特性和c++高階主題(3)

程式設計與演算法(三)第十週 c++新特性和c++高階主題(3)

強制型別轉換 :static_cast、interpret_cast、const_cast、dynamic_cast

1、static_cast:static_cast 用來進行比較“自然”和低風險的轉換,比如整型和實數型、字元型之間的互相轉換。static_cast不能用來在不同型別的指標之間互相轉換,也不能用於不同型別的引用之間的轉換,也不能用於不同型別的引用之間的轉換。

#include <iostream>
using namespace std;
class A{
public:
    operator int(){return 1;}
    operator char
*(){return NULL;} }; int main() { A a; int n; char *p="New Dragon Inn"; n = static_cast<int>(3.14); n = static_cast<int>(a); p = static_cast<char *>(a); // n = static_cast<int>(p);//編譯錯誤,static_cast不能將指標轉換成整型 // p = static_cast<char*>(n);//編譯錯誤,static_cast不能將整型轉換成指標
return 0; }

2、reinterpret_cast: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);
    int& r = reinterpret_cast<int&
>(a); r=200;//將a.i改變成了200 cout<<a.i<<","<<a.j<<endl; int n=300; A*pa=reinterpret_cast<A*>(&n); pa->i=400; pa->j=500; cout<<n<<endl; long long la=0x12345678abcdLL; pa = reinterpret_cast<A*>(la);//la 太長,只取低32位5678abcd拷貝給pa //下面兩句在課程中是貌似是可以的,不過,在我電腦上有問題。。 // unsigned int u = reinterpret_cast<unsigned int>(pa); // cout<<hex<<u<<endl; typedef void(*PF1)(int); typedef int(*PF2)(int, char *); PF1 pf1; PF2 pf2; pf2 = reinterpret_cast<PF2>(pf1); } //輸出: /* 200,100 400 */

3、const_cast:用來進行去除const屬性的轉換。將const引用轉換成同類型的非const引用。將const指標轉換為同類型的非const指標時用題它,如:

const string s="Inception";
string &p=const_cast<string &>(s);
string *ps = const_cast<string *>(&s);

4、dynamic_cast

dynamic_cast專門用於將多型基類指標或引用,強制轉換成派生類的指標或引用,而且能夠檢測轉換的安全性。對於不安全的指標轉換,轉換結果返回NULL指標。dynamic_cast不能用於將非多型基類的指標或引用,強制轉換為派生類的指標或引用。(多型基類:包含虛擬函式的基類)

#include <iostream>
#include <string>
using namespace std;
// 多型基類
class Base{
public:
    virtual ~Base(){}
};
class Derived:public Base{};
int main()
{
    Base b;
    Derived d;
    Derived *pd;
    pd = reinterpret_cast<Derived*>(&b);
    if(pd==NULL)
    {
        //此處pd不會為NULL
        // reinterpret_cast不檢測安全性,總是進行轉換
        cout<<"unsafe"<<endl;
        //不會執行
    }
    pd = dynamic_cast<Derived*>(&b);
    if(pd==NULL)
    {
        //結果會是NULL因為&b不是指向派生類物件,此處轉換不安全
        cout<<"unsafe dynamic_cast 1"<<endl;
    }
    Base *pb=&d;
    pd = dynamic_cast<Derived*>(pb);
    if(pd==NULL)
    {
        cout<<"unsafe dynamic_cast 2"<<endl;
    }
    return 0;
}
/*
 * 輸出:
unsafe dynamic_cast 1
 */

dynamic_cast:實現基類引用到派生類引用之間的轉換

Derived & r=dynamic_cast<Derived &>(b);

如何判斷改轉換是否安全呢?答案:不安全則丟擲異常

異常處理

在這裡插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    double m, n;
    cin>>m>>n;
    try{
        // try 塊
        cout<<"before dividing."<<endl;
        // 一旦到throw那try就停止執行,進catch塊
        //
        if(n==0)
            throw -1;//丟擲int型別異常,
        else if(m==0)
            throw -1.0;
        else
            cout<<m/n<<endl;
        cout<<"after dividing"<<endl;
    }
        //catch從上到下檢測
    catch(double d) {
        //捕獲double型異常
        cout<<"catch(double)"<<d<<endl;
    }
    catch(int e){
        //捕獲整型異常
        cout<<"catch(int)"<<e<<endl;
    }
    catch(...){
        //捕獲任何型別的異常
        cout<<"catch(...)"<<endl;
    }
    cout<<"finished"<<endl;
    return 0;
}
0 6
before dividing.
catch(double)-1
finished

異常的再丟擲

如果一個函式在執行的過程中,丟擲的異常在本函式內就被catch塊捕獲並處理了,那麼該異常就不會拋給這個函式的呼叫者(也稱“上一層的函式”);如果異常在本函式中沒被處理,就會被拋給上一層的函式

#include <iostream>
#include <string>
using namespace std;
class CException {
public:
    string msg;
    CException(string s) : msg(s) {}
};
double Devide(double x, double y) {
    if (y == 0)
        throw CException("devide by zero");
    cout << "in Devide" << endl;
    return x / y;
}

int CountTax(int salary){
    try{
        if(salary<0)
            throw -1;
        cout<<"counting tax"<<endl;
    }
    catch(int)
    {
        cout<<"salary<0"<<endl;
    }
    cout<<"tax counted"<<endl;
    return salary*0.15;
}
int main()
{
    double f=1.2;
    try {
        //下面這個異常被處理了
        CountTax(-1);
        //下面這個異常被捕獲
        f=Devide(3,0);
        cout<<"end of try block"<<endl;
    }
    catch(CException e) {

        cout<<"in main catch "<<e.msg<<endl;
    }
    cout<<"f="<<f<<endl;
    cout<<"finished"<<endl;
    return 0;

}
salary<0
tax counted
in main catch devide by zero
f=1.2
finished

c++標準異常類:c++標準庫中有一些類代表異常,這些類都是從exception類派生而來。常用的幾個異常類如下:

在這裡插入圖片描述

bad_cast:在用dynamic_cast進行從多型基類物件(或引用),到派生類的引用的強制型別轉換時,如果轉換是不安全的,則會丟擲此異常。

#include <iostream>
#include <stdexcept>
#include <typeinfo>
using namespace std;
class Base{
    virtual void func(){}
};
class Derived:public Base{
public:
    void Print(){}
};
void PrintObj(Base& b)
{
    try{
        //基類引用強制轉為派生類引用
        Derived& rd = dynamic_cast<Derived&>(b);
        // 此轉換若不安全,會丟擲bad_cast異常
        rd.Print();
    }
    catch(bad_cast& e){
        cerr<<e.what()<<endl;
    }
}
int main()
{
    Base b;
    PrintObj(b);
    return 0;
}
// 輸出:std::bad_cast

bad_alloc:在用new運算子進行動態記憶體分配時,如果沒有足夠的記憶體則會引發此異常。

#include <iostream>
#include <stdexcept>
using namespace std;
int main()
{
    try{
        char *p = new char[0x7ffffffff];
        //無法分配這些空間,會拋異常
    }
    catch(bad_alloc& e)
    {
        cerr<<e.what()<<endl;
    }
    return 0;
}
// 輸出:std::bad_alloc

out_of_range 用vector或string的at成員函式根據下標訪問元素時,如果下標越界,就會丟擲此異常。例如:

#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
using namespace std;
int main()
{
    vector<int> v(10);
    try{
        // 如果[]不檢測下標越界,at()會檢測,所以速度慢
        v.at(100)=100;
        // 丟擲out_of_range的異常
    }
    catch(out_of_range& e){
        cerr<<e.what()<<endl;
    }
    string s="hello";
    try{
        char c=s.at(100);
        //丟擲out_of_range的異常
    }
    catch(out_of_range& e){
        cerr<<e.what()<<endl;
    }

    return 0;
}
//輸出:
// vector::_M_range_check: __n (which is 100) >= this->size() (which is 10)
// basic_string::at: __n (which is 100) >= this->size() (which is 5)

到此,所有課程終於結束,感謝老師的熱情講解,以後繼續學習c++吧!