【ThinkingInC++】69、异常处理

第一章 异常处理

1.5清理

1.5.1 资源管理

如果一个对象的构造函数在执行过程中抛出异常,那么这个对象的析构函数就不会被调用。

Rawp.cpp

/**
* 书本:【ThinkingInC++】
* 功能:资源管理
* 时间:2014年10月8日20:19:03
* 作者:cutter_point
*/

#include <iostream>
#include <cstddef>

using namespace std;

class Cat
{
public:
    Cat() { cout<<"Cat()"<<endl; }
    ~Cat() { cout<<"~Cat()"<<endl; }
};

class Dog
{
public:
    void* operator new(size_t sz)
    {
        cout<<"分配一个Dog的空间"<<endl;
        throw 47;   //抛出异常,后面为了检验构造函数抛出异常的后果
    }
    void operator delete(void* p)
    {
        cout<<"回收一个Dog的空间"<<endl;
        ::operator delete(p);
    }
};

class UseResources
{
    Cat* bp;
    Dog* op;    //这个事类的组合
public:
    UseResources(int count=1)
    {
        cout<<"UseResource的构造函数"<<endl;
        bp=new Cat[count];
        op=new Dog;
    }
    ~UseResources()
    {
        cout<<"~UseResources的析构函数"<<endl;
        delete [] bp;   //回收数组空间
        delete op;
    }
};

int main()
{
    try
    {
        UseResources ur(3);
    }
    catch(int)
    {
        cout<<"inside handler"<<endl;
    }

    return 0;
}


 

 

1.5.2 使所有事物都成为对象

·在构造函数中捕获

·在对象的构造函数中分配资源,并且在对象的析构函数中释放资源。

Wrapped.cpp

/**
* 书本:【ThinkingInC++】
* 功能:使所有事物都成为对象,防止资源泄露
* 时间:2014年10月8日20:19:38
* 作者:cutter_point
*/

#include <iostream>
#include <cstddef>

using namespace std;

template<class T, int sz=1>
class PWrap
{
    T* ptr; //一个这个类型的指针
public:
    class RangeError {};    //这是一个异常类
    PWrap()
    {
        ptr=new T[sz];  //构造函数,给数据成员创建初值
        cout<<"PWrap的构造函数"<<endl;
    }
    ~PWrap()
    {
        delete [] ptr;  //析构
        cout<<"PWrap的析构函数"<<endl;
    }
    T& operator [] (int i) throw(RangeError)    //抛出一个RangeError对象的异常
    {
       if(i >= 0 && i < sz) return ptr[i];
       throw RangeError();  //超出范围就抛出异常
    }
};

class Cat
{
public:
    Cat() { cout<<"Cat()猫的构造函数"<<endl; }
    ~Cat() { cout<<"~Cat()猫的析构函数"<<endl; }
    void g() {}
};


class Dog
{
public:
    void* operator new[](size_t)
    {
        cout<<"分配狗的空间"<<endl;
        throw 47;   //内存不够抛出异常
    }
    void operator delete[](void* p)
    {
        cout<<"回收狗的内存空间"<<endl;
        ::operator delete[](p);
    }
};

class UseResources
{
    PWrap<Cat, 3> cats; //创建3只猫
    PWrap<Dog> dog;
public:
    UseResources() { cout<<"UseResources构造函数"<<endl; }
    ~UseResources() { cout<<"~UseResources析构函数"<<endl; }
    void f() { cats[1].g(); }
};

int main()
{
    try
    {
        UseResources ur;
    }
    catch(int)
    {
        cout<<"抛出异常,捕获一个整形的数值"<<endl;
    }
    catch(...)
    {
        cout<<"其他的捕获(...)"<<endl;
    }

    return 0;
}


 

 

 

 

1.5.3 auto_ptr

RAII:资源获得式初始化

1.7 异常规格说明

Unexcepted.cpp

/**
* 书本:【ThinkingInC++】
* 功能:关于设置自己的异常函数,来显示不可预知的异常
* 时间:2014年10月8日20:20:06
* 作者:cutter_point
*/

#include <exception>
#include <iostream>
#include <cstdlib>

using namespace std;

class Up {};
class Fit {};
void g();

void f(int i) throw(Up, Fit)    //根据给的int值来抛出异常
{
    switch(i)
    {
    case 1: throw Up();
    case 2: throw Fit();
    }
    g();    //本来在版本一的g()只是被声明的时候是不会抛出任何异常的,但是后面又抛出了一个int型的异常
}

//定义函数g()   所以这里违反了异常抛出的规格(只有两种)
void g() { throw 47; }

void my_unexcepted()    //这个用来设置在规格之外的异常抛出
{
    cout<<"规格之外的异常抛出,不是Up也不是Fit"<<endl;
    exit(0);
}

int main()
{
    set_unexpected(my_unexcepted);    //设置规格之外的异常将会调用的函数,忽略返回值
    for(int i=1 ; i <= 3 ; ++i)
        try
        {
            f(i);
        }
        catch(Up)
        {
            cout<<"抛出Up类型的异常"<<endl;
        }
        catch(Fit)
        {
            cout<<"抛出Fit类型的异常"<<endl;
        }

    return 0;
}



 

 

1.8 异常安全

对于赋值操作符。

1)确保程序不是给自己赋值。如果是的话,到步骤6

2)给指针数据成员分配所需的新内存

3)从原来的内存区间向新分配的内存区拷贝数据

4)释放原有的内存

5)更新对象的状态,也就是把指向分配新堆内存地址的指针赋值给指针数据成员

6)返回*this

 

关于句柄

从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。

 

管理其他资源的类

 

Strlen

需要明确的第一点,strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符‘/0‘为止,然后返回计数器值。

 

SafeAssign.cpp

 

/**
* 书本:【ThinkingInC++】
* 功能:关于operator=的安全分配内存
* 时间:2014年10月8日20:20:41
* 作者:cutter_point
*/

#include <iostream>
#include <new>  //为了抛出异常bad_alloc
#include <cstring>
#include <cstddef>

using namespace std;

class HasPointers
{
    //这里设置一个句柄类来管理我们的数据
    struct MyData
    {
        const char* theString;
        const int* theInts;
        size_t numInts;
        //构造函数
        MyData(const char* pString, const int* pInts, size_t nInts) : theString(pString), theInts(pInts), numInts(nInts) {}
    }*theData;  //这个指针是指向这个结构体的
    //这里为了安全的分配内存,我们使用一个clone静态函数
    static MyData* clone(const char* otherString, const int* otherInts, size_t nInts)
    {
        /*
        需要明确的第一点,strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,
        中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符'/0'为止,然后返回计数器值。
        */
        char* newChars=new char[strlen(otherString)+1]; //吧这个字符串的长度加一,作为这个数组的长度,最后一位是'/0'
        int* newInts;
        //这里分配int类型的,避免异常
        try
        {
            newInts=new int[nInts];
        }
        catch(bad_alloc&)
        {
            delete []newChars;
            throw;
        }
        try
        {
            strcpy(newChars, otherString);  //拷贝到newChars里面去
            for(size_t i=0 ; i < nInts ; ++i)
                newInts[i]=otherInts[i];    //吧otherInts里面的数据赋值给newInts
        }
        catch(...)
        {
            delete []newInts;
            delete []newChars;
            throw;
        }
        return new MyData(newChars, newInts, nInts);
    }

    //一个重载的clone,拷贝构造函数
    static MyData* clone(const MyData* otherData)
    {
        return clone(otherData->theString, otherData->theInts, otherData->numInts);
    }
    //内存清理
    static void cleanup(const MyData* theData)
    {
        //回收全部内存,只要使用了指针的,都要把指针指向的内存回收掉
        delete theData->theString;
        delete theData->theInts;
        delete theData;
    }
public:
    //构造函数
    HasPointers(const char* someString, const int* someInts, size_t numInts)
    {
        theData=clone(someString, someInts, numInts);
    }
    //拷贝构造函数
    HasPointers(const HasPointers& source) { theData=clone(source.theData); }
    //赋值拷贝运算符
    HasPointers& operator=(const HasPointers& rhs)
    {
        //避免自赋值
        if(this != &rhs)
        {
            MyData* newData=clone(rhs.theData->theString, rhs.theData->theInts, rhs.theData->numInts);
            cleanup(theData);
            theData=newData;
        }
        return *this;
    }
    //析构函数
    ~HasPointers() { cleanup(theData); }
    //友元函数
    friend ostream& operator<<(ostream& os, const HasPointers& obj)
    {
        os<<obj.theData->theString<<" : ";
        for(size_t i=0 ; i < obj.theData->numInts ; ++i)
            os<<obj.theData->theInts[i]<<' ';
        return os;
    }
};

int main()
{
    int someNums[]={1, 2, 3, 4};
    size_t someCount=sizeof(someNums)/sizeof(someNums[0]);
    int someMoreNums[]={5, 6, 7};
    size_t someMoreCount=sizeof(someMoreNums)/sizeof(someMoreNums[0]);
    cout<<"这两个数组长度是:"<<someCount<<" : "<<someMoreCount<<endl;
    HasPointers h1("Hello", someNums, someCount);
    HasPointers h2("Goodbye", someMoreNums, someMoreCount);
    cout<<h1<<endl;
    h1=h2;
    cout<<h1<<endl;

    return 0;
}


 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。