【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;
}
时间: 2024-11-08 20:29:36

【ThinkingInC++】69、异常处理的相关文章

(69)Python异常处理与断言

http://blog.csdn.net/pipisorry/article/details/21841883 断言 断言是一句必须等价于布尔真的判定;此外,发生异常也意味着表达式为假.这些工作类似于 C 语言预处理器中 assert 宏,但在 Python 中它们在运行时构建(与之相对的是编译期判别).如果你刚刚接触断言这个概念,无妨.断言可以简简单单的想象为 raise-if 语句(更准确的说是raise-if-not 语句).测试一个表达式,如果返回值是假,触发异常. 断言语句等价于这样的

java-10异常处理动手动脑

1.请阅读并运行AboutException.java示例,然后通过后面的几页PPT了解Java中实现异常处理的基础知识. import javax.swing.*; class AboutException { public static void main(String[] a) { int i=1, j=0, k; k=i/j; try { k = i/j; // Causes division-by-zero exception //throw new Exception("Hello.

Java: 异常处理机制

1. 如何捕获异常 try { 可能会出现异常的代码段: } catch(异常类型名 处理该异常对象) { 异常处理代码段: } 1 import java.io.*; 2 3 public class TryCatchTest { 4 5 public static void main(String[] args) { 6 File file = new File("abc.txt"); 7 int a[] = {1, 2}; 8 9 try 10 { 11 System.out.p

69 个经典 Spring 面试题和答案

Spring 概述 1. 什么是spring? Spring 是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯. 2. 使用Spring框架的好处是什么? 轻量:Spring 是轻量的,基本的版本大约2MB. 控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们. 面向切面的

NET MVC异常处理模块

一个简单的ASP.NET MVC异常处理模块 一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都会使用到它,无论是分层中的哪一个层,还是具体的哪个业务逻辑模块,所关注的都是一样的.所以,横切关注点我们会统一在一个地方进行处理.无论是MVC还是WebForm都提供了这样实现,让我们可以集中处理异常. 在MVC中,在FilterConfig中,已经默认

Java学习之异常处理

*异常的注意事项:1,子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类.2,如果父类抛出多个异常,那么子类只能抛出父类异常的子集.简单说:子类覆盖父类只能抛出父类的异常或者子类或者子集. 注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try . *异常处理的原则:1,函数内容如果抛出需要检测的异常,那么函数上必须要声明. 否则必须在函数内用trycatch捕捉,否则编译失败.2,如果调用到了声明异常的函数,要么trycatch要

【转】异常处理模块

一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都会使用到它,无论是分层中的哪一个层,还是具体的哪个业务逻辑模块,所关注的都是一样的.所以,横切关注点我们会统一在一个地方进行处理.无论是MVC还是WebForm都提供了这样实现,让我们可以集中处理异常. 在MVC中,在FilterConfig中,已经默认帮我们注册了一个HandleErrorAttr

异常处理的课后

异常处理的目的是依据实际情况提供不同的错误应对策略与手段,使程序更稳定,更安全. 异常处理的主要用途是提供准确的错误消息,解释失败的原因.位置和错误类型等,同时提供一定的恢复能力,尽可能地保证数据完整性不被破坏,并让程序能继续运行. Try{ //可能发生运行错误的代码: } catch(异常类型     异常对象引用){ //用于处理异常的代码 } finally{ //用于"善后" 的代码 } 把可能会发生错误的代码放进try语句块中. 当程序检测到出现了一个错误时会抛出一个异常对

Java编程思想学习(九) 异常处理

java的异常处理机制可以使程序有极好的容错性,让程序更加的健壮.所谓的异常,就是指的阻止当前方法或作用域继续执行的问题,,当程序运行时出现异常时,系统就会自动生成一个Exception对象来通知程序.这样就极大的简化了我们的工作. 当然java的异常对象有很多种,下面这幅图显示了java异常类的继承体系.  从图片中可以看到java将所有的非正常情况分成了两种: 异常(Exception)和错误(Error),他们都继承Throwable父类.Error错误一般是指与虚拟机相关的问题,如系统崩