Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43700
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 176
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-31 01:57
个人简介

资深码农

文章分类

全部博文(15)

文章存档

2016年(3)

2014年(12)

我的朋友

分类: C/C++

2014-02-06 17:46:51

C语言的老手对以下这种代码风格并不陌生

点击(此处)折叠或打开

  1. void setName(char* name)
  2. {
  3.     if (!name)
  4.     {
  5.         printf("NULL pointer [%s] [%d]". __FILE__, __LINE__);
  6.         return;
  7.     }
  8. }
当输入参数是NULL的时候,打印一条错误信息,并且返回。不过这样做会有一些问题,比如说代码写的比较啰嗦,如果一个工程中到处都是这种判断错误的代码,那么代码膨胀的比较厉害。让我们看一个简单的exception的例子

点击(此处)折叠或打开

  1. #include "stdafx.h"
  2. #include <stdio.h>
  3. #include <iostream>
  4. #include <vector>
  5. #include <string>
  6. #include <stdexcept>

  7. using namespace std;

  8. class MyError : public exception
  9. {
  10.     string ErrorName;
  11. public:
  12.     MyError(string aErrorName)
  13.         : ErrorName(aErrorName)
  14.     {
  15.         cout << "MyError() called" << endl;
  16.     }
  17.     virtual ~MyError()
  18.     {
  19.         cout << "~MyError() called" << endl;
  20.     }
  21.     string what()
  22.     {
  23.         return ErrorName;
  24.     }
  25. };

  26. void testException(char* input)
  27. {
  28.     try
  29.     {
  30.         if (!input)
  31.         {
  32.             throw MyError("NULL input in testException");
  33.         }
  34.     }
  35.     catch (MyError& aError)
  36.     {
  37.         cout << aError.what() << endl;
  38.     }
  39.     catch (...)
  40.     {
  41.         cout << "Exception caught in testException()" << endl;
  42.     }
  43. }

  44. int _tmain(int argc, _TCHAR* argv[])
  45. {
  46.     testException(NULL);
  47.     system("pause");
  48.     return 0;
  49. }
第10行定义一个类MyError,里面存储一个记录异常信息的字符串,第35行抛出MyError类型的异常,第38行捕获MyError类型的异常,如果捕获成功则打印异常信息what();如果抛出的异常不是MyError类型,则第42行的catch (...)形式的代码将发挥作用,"..."代表即将捕获的异常类型是任意类型的。win32上运行程序,打印输出如下所示
MyError() called
~MyError() called
NULL input in testException
~MyError() called
如果抛出异常而没被捕获会发生什么呢?让我们做个实验,把testException函数改一下,在第一个catch里面加上一行代码throw,这种后面紧跟分号,并且出现在catch中的throw表明程序在重新抛出异常,这个异常将无法被本身的try...catch所捕获。

点击(此处)折叠或打开

  1. void testException(char* input)
  2. {
  3.     try
  4.     {
  5.         if (!input)
  6.         {
  7.             throw MyError("NULL input in testException");
  8.         }
  9.     }
  10.     catch (MyError& aError)
  11.     {
  12.         cout << aError.what() << endl;
  13.        throw;
  14.     }
  15.     catch (...)
  16.     {
  17.         cout << "Exception caught in testException()" << endl;
  18.     }
  19. }
运行程序,会看到程序被终止了,这是因为程序遇到了一个没有被捕获的异常,则调用了abort()。

到目前为止,我们了解了关于异常的一些基本知识,但事实上,我们并未触及C++异常的精髓部分,下面让我们看一个例子,

点击(此处)折叠或打开

  1. #include "stdafx.h"
  2. #include <stdio.h>
  3. #include <iostream>
  4. #include <vector>
  5. #include <string>
  6. #include <stdexcept>

  7. using namespace std;

  8. class Name
  9. {
  10.     char * name;
  11.     size_t mySize;
  12. public:
  13.     Name(char* pName, size_t size) : mySize(size)
  14.     {
  15.         cout << "CTOR of Name() called" << endl;
  16.         name = new char[size];
  17.         for (char * p1 = pName, * p2 = name; size > 0; size--)
  18.             *p2++ = *p1++;
  19.     }
  20.     virtual ~Name()
  21.     {
  22.         cout << "DTOR of Name() called" << endl;
  23.         delete []name;
  24.     }
  25.     void print()
  26.     {
  27.         size_t aSize = mySize;
  28.         for (char* p = name; aSize > 0; aSize--)
  29.             cout << *p++;
  30.     }
  31. };

  32. void testException(char* input)
  33. {
  34.     try
  35.     {
  36.         char myName[] = "Michael";
  37.         Name *aName = new Name(myName, sizeof(myName));
  38.         aName->print();
  39.         if (!input)
  40.         {
  41.             throw runtime_error("NULL input");
  42.         }
  43.     }
  44.     catch (...)
  45.     {
  46.         cout << "Exception caught in testException()" << endl;
  47.     }
  48. }

  49. int _tmain(int argc, _TCHAR* argv[])
  50. {
  51.     testException(NULL);
  52.     system("pause");
  53.     return 0;
  54. }
第40行new一个Name的对象,aName指向这个对象,第44行抛出异常,然后程序直接跳转到第49行,以下是程序的输出,
CTOR of Name() called
Michael
我们看到,Name类本身的析构函数没有被调用,而这也就意味着内存发生了泄露!这是C/C++程序员最不愿因看到的。尤其在大型的项目上,追查一个内存泄露bug经常要耗费数日。好吧,那我们看看如何避免这类问题的发生呢?最简单的办法是把testException函数做一个小小的改动,在抛出异常之前delete aName,

点击(此处)折叠或打开

  1. void testException(char* input)
  2. {
  3.     try
  4.     {
  5.         char myName[] = "Michael";
  6.         Name *aName = new Name(myName, sizeof(myName));
  7.         aName->print();
  8.         if (!input)
  9.         {
  10.             delete aName;
  11.             throw runtime_error("NULL input");
  12.         }
  13.     }
  14.     catch (...)
  15.     {
  16.         cout << "Exception caught in testException()" << endl;
  17.     }
  18. }
这样程序的输出就变为
CTOR of Name() called
Michael DTOR of Name() called
这样就避免了内存泄露。

如果异常发生在构造函数内(因为C++的构造函数没有返回值,因此构造函数里抛出异常时非常常见的),那么会如何呢?其实道理都是一样的,一旦发生异常,那程序会直接跳转到catch这个异常的地方,当然也就不会调用析构函数了,因此在构造函数内异常抛出的地方之前如果发生过new操作,那么一定要在抛异常之前调用相应的delete进行内存释放,不过程序员们经常会遗漏delete,那有没有什么好的方法彻底杜绝内存泄露呢?
当然有,看下面这个例子,

点击(此处)折叠或打开

  1. void testException(char* input)
  2. {
  3.     try
  4.     {
  5.         char myName[] = "Michael";
  6.         shared_ptr<Name> aName(new Name(myName, sizeof(myName)));
  7.         aName->print();
  8.         if (!input)
  9.         {
  10.             throw runtime_error("NULL input");
  11.         }
  12.     }
  13.     catch (...)
  14.     {
  15.         cout << "Exception caught in testException()" << endl;
  16.     }
  17. }
这个例子中使用了shared_ptr,c++的智能指针(使用shared_ptr请记得包含头文件),shared_ptr非常强大,这里不做详细说明了,使用shared_ptr的话,把需要定义的类对象(在这里是Name类)当做模板的class T传入,然后在定义对象的后面的括号里new就可以了,使用这种智能指针最大的好处在于,程序员不需要再去关心new以后的delete了,因为shared_ptr里面封装了一套机制,当程序运行脱离了现有的作用域的时候,会自动delete之前new的对象。









阅读(857) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~