C++异常rethrow【转】

转自

http://se.csai.cn/ExpertEyes/200801031114531905.htm


在相遇篇中的《第5集 C++的异常rethrow》文章中,已经比较详细讨论了异常重新被抛出的处理过程。但是有一点却并没有叙述到,那就是C++异常重新被抛出时(rethrow),异常对象的构造、传递和析构销毁的过程会有哪些变化和不同之处。为了精益求精,力求对每一个细节都深入了解和掌握,下面再全面阐述一下各种不同组合情况下的异常构造和析构的过程。

  大家现在知道,异常的重新被抛出有两种方式。其一,由于当前的catch block块处理不了这个异常,所以这个异常对象再次原封不动地被重新抛出;其二,就是在当前的catch block块处理异常时,又激发了另外一个异常的抛出。另外,由于异常对象的传递方式有三种:传值、传引用和传指针。所以实际上这就导致了有6种不同的组合情况。下面分别阐述之。

  异常对象再次原封不动地被重新抛出

  1、首先讨论异常对象“按值传递”的方式下,异常对象的构造、传递和析构销毁的过程有何不同之处?毫无疑问,在异常被重新被抛出时,前面的一个异常对象的构造和传递过程肯定不会被影响,也即“按值传递”的方式下,异常被构造了3次,异常对象被“按值传递”到这个catch block中。实际上,需要研究的是,当异常被重新被抛出时,这个异常对象是否在离开当前的这个catch block域时会析构销毁掉,并且这个异常对象是否还会再次被复制构造?以及重新被抛出的异常对象按什么方式被传递?看如下例程:

  class MyException

  {

  public:

  MyException (string name="none") : m_name(name)

  {

  number = ++count;

  cout << "构造一个MyException异常对象,名称为:"<<<":"<<< p="" endl;<=""> <<":"<<<>

  }

  MyException (const MyException& old_e)

  {

  m_name = old_e.m_name;

  number = ++count;

  cout << "拷贝一个MyException异常对象,名称为:"<<<":"<<< p="" endl;<=""> <<":"<<<>

  }

  operator= (const MyException& old_e)

  {

  m_name = old_e.m_name;

  number = ++count;

  cout << "赋值拷贝一个MyException异常对象,名称为:"<<<":"<<< p="" endl;<=""> <<":"<<<>

  }

  virtual ~ MyException ()

  {

  cout << "销毁一个MyException异常对象,名称为:" <<<":"<<< p="" endl;<=""> <<":"<<<>

  }

  string GetName()

  {

  char tmp[20];

  memset(tmp, 0, sizeof(tmp));

  sprintf(tmp, "%s:%d", m_name.c_str(), number);

  return tmp;

  }

  virtual string Test_Virtual_Func() { return "这是MyException类型的异常对象";}

  protected:

  string m_name;

  int number;

  static int count;

  };

  int MyException::count = 0;

  void main()

  {

  try

  {

  try

  {

  // 抛出一个异常对象

  throw MyException("ex_obj1");

  }

  // 异常对象按值传递

  catch(MyException e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  cout<<"下面重新抛出异常"<<< p=""> <<>

  // 异常对象重新被抛出

  throw;

  }

  }

  // 异常对象再次按值传递

  catch(MyException e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  }

  }

  程序运行的结果是:

  构造一个MyException异常对象,名称为:ex_obj1:1

  拷贝一个MyException异常对象,名称为:ex_obj1:2

  拷贝一个MyException异常对象,名称为:ex_obj1:3

  销毁一个MyException异常对象,名称为:ex_obj1:1

  捕获到一个MyException*类型的异常,名称为:ex_obj1:3

  下面重新抛出异常

  拷贝一个MyException异常对象,名称为:ex_obj1:4

  销毁一个MyException异常对象,名称为:ex_obj1:3

  捕获到一个MyException*类型的异常,名称为:ex_obj1:4

  销毁一个MyException异常对象,名称为:ex_obj1:4

  销毁一个MyException异常对象,名称为:ex_obj1:2

  通过上面的程序运行结果,可以很明显地看出,异常对象在被重新抛出时,又有了一次拷贝复制的过程,瞧瞧!正常情况下,按值传递异常的方式应该是有3次构造对象的过程,可现在有了4次。那么这个异常对象在什么时候又再次被复制构造的呢?仔细分析一下,其实也不难明白, “异常对象ex_obj1:1”是局部变量;“异常对象ex_obj1:2”是临时变量;“异常对象ex_obj1:3”是第一个(内层的)catch block中的参数变量。当在catch block中再次throw异常对象时,它会即刻准备离开当前的catch block域,继续往上搜索对应的catch block模块,找到后,即完成异常对象的又一次复制构造过程,也即把异常对象传递给上一层的catch block域中。之后,正式离开内层的catch block域,并析构销毁这个catch block域中的异常对象ex_obj1:3,注意此时,属于临时变量形式的异常对象ex_obj1:2并没有被析构,而是直至到后一个catch block处理完后,先析构销毁异常对象ex_obj1:4,再才销毁异常对象ex_obj1:2。整个程序的执行流程如图14-1所示。

  

  图14-1异常对象构造和销毁的过程

  下面来一步一步看它的流程,第①步执行的操作,及执行完它之后的状态,如图14-2所示。它构造了一个局部变量形式的异常对象和拷贝复制了一个临时变量形式的异常对象。

  

  图14-2 第一步,抛出异常

  由于第①步执行的抛出异常的操作,因此找到了相应的catch block后,便执行第②步复制异常对象的过程,如图14-3所示。

  

  图14-3 第二步,复制异常到catch block域

  第②步复制异常对象完毕后,便进入到第③步,离开原来抛出异常的作用域,如图14-4所示。这一步的操作是由系统所完成的,主要析构销毁这个当前作用域已经构造过的对象,其中也包括属于局部变量的异常对象。

  

  图14-4 第三步,析构局部变量

  接下来正式进入到catch block的异常处理模块中,如图14-5所示。

  

  图14-5 第四步,异常处理模块中

  当在异常模块中再次原封不动地把原来的异常对象重新抛出,那么系统将会继续下一次的查找catch block模块的过程,如图14-6所示。注意,它这里并不会再次复制一个另外的临时异常对象,而只是在新的catch block模块中完成一次异常对象的复制过程。

  

  图14-6 第五步,又一次复制异常到catch block域

  同样,在复制完毕异常对象以后。程序控制流又会回到原来的作用域去销毁局部变量。如图14-7所示。注意,这里并不会析构销毁临时变量的异常对象,而只是销毁当前作用域内部的局部变量,如“异常对象ex_obj1:3”。

  

  图14-7 第六步,离开前面的catch block作用域,并析构该作用域范围内的局部变量

  再接下来就是进入到又一次的异常处理模块中,如图14-8所示。注意,此时系统中存在

  “异常对象ex_obj1:2”和“异常对象ex_obj1:4”。

  

  图14-8 第七步,又一次的异常处理模块中

  最后就是全部处理完毕,如图14-9所示。注意,它先析构销毁“异常对象ex_obj1:4”,再才销毁“异常对象ex_obj1:2”。

  

  图14-9 第八步,销毁另外的两个异常对象

  通过以上可以清晰地看出,在“按值传递”的方式下,异常对象的被重新rethrow后,它的执行过程虽然与正常抛出异常的情况虽然有所差异,但是在原理上,它们完全是相一致的,而且异常的rethrow是可以不断地向上抛出,就好像是接力赛一样,同时,每再抛出一次后,异常对象将会被复制构造一次。所以说,这里更进一步说明了异常对象的“按值传递”的方式是效率很低的。但是亲爱的程序员朋友们,大家是否也象那个有点傻气,但好像又有些灵气的主人公阿愚一样,联想到了另外一种有些奇怪的组合方式,那就是如果异常对象第一次是“按值传递”的方式,但第二、第三次,甚至后来的更多次,是否可以按其它方式(如“按引用传递” 的方式)呢?如果可以,那么又会出现什么结果呢?还是先看看示例吧!上面的程序只作了一点改动,如下:

  void main()

  {

  try

  {

  try

  {

  // 抛出一个异常对象

  throw MyException("ex_obj1");

  }

  // 异常对象按值传递

  catch(MyException e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  cout<<"下面重新抛出异常"<<< p=""> <<>

  // 异常对象重新被抛出

  throw;

  }

  }

  // 注意,这里改为按引用传递的方式

  catch(MyException& e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  }

  }

  程序运行的结果是:

  构造一个MyException异常对象,名称为:ex_obj1:1

  拷贝一个MyException异常对象,名称为:ex_obj1:2

  拷贝一个MyException异常对象,名称为:ex_obj1:3

  销毁一个MyException异常对象,名称为:ex_obj1:1

  捕获到一个MyException*类型的异常,名称为:ex_obj1:3

  下面重新抛出异常

  销毁一个MyException异常对象,名称为:ex_obj1:3

  捕获到一个MyException*类型的异常,名称为:ex_obj1:2

  销毁一个MyException异常对象,名称为:ex_obj1:2

  哈哈!主人公阿愚非常开心。因为它果然不出所料,是完全可以的,而且结果也合乎情理。异常对象还是只构造了三次,并未因为异常的再次抛出,而多复制构造一次异常对象。实际上,大家已经知道,控制异常对象的传递方式是由catch block后面的参数所决定,所以对于无论是最初抛出的异常,还是异常在catch block块被再次抛出,它们无须来关心,也控制不了。在最初抛出的异常时,完成两次异常对象的构造过程,其中最重要的是临时的异常对象,它是提供向其它参数形式的异常对象复制构造的原型,也即异常在不断接力地被抛出之后,如果上层的某个catch block定义“按值传递”的方式,那么系统就会从这个临时变量的异常对象复制一份;如果上层的某个catch block定义“按引用传递”的方式,那么系统会把引用指向这个临时变量的异常对象。而这个临时变量的异常对象,只有在最后一个catch block块(也即没有再次抛出)执行处理完毕之后,才会把这个异常对象予以析构销毁(实际上,在这里销毁是最恰当的,因为异常的重新被抛出,表明这个异常还没有被处理完毕,所以只有到最后一个catch block之后,这个临时变量的异常对象才真正不需要了)。

  另外,还有一点需要进一步阐述,那就是上层的某个catch block定义“按值传递”的方式下,系统从临时变量的异常对象所复制一份参数形式的异常对象,它一定会在它这个作用域无效时,把它给析构销毁掉。

  2、接下来,讨论异常对象“按引用传递”的方式下,异常对象的构造、传递和析构销毁的过程有何不同之处?其实这在刚才已经详细讨论过了,不过,还是看看例程来验证一下,如下:

  void main()

  {

  try

  {

  try

  {

  // 抛出一个异常对象

  throw MyException("ex_obj1");

  }

  // 这里改为按引用传递的方式

  catch(MyException& e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  cout<<"下面重新抛出异常"<<< p=""> <<>

  // 异常对象重新被抛出

  throw;

  }

  }

  // 这里改为按引用传递的方式

  catch(MyException& e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  }

  }

  程序运行的结果是:

  构造一个MyException异常对象,名称为:ex_obj1:1

  拷贝一个MyException异常对象,名称为:ex_obj1:2

  销毁一个MyException异常对象,名称为:ex_obj1:1

  捕获到一个MyException*类型的异常,名称为:ex_obj1:2

  下面重新抛出异常

  捕获到一个MyException*类型的异常,名称为:ex_obj1:2

  销毁一个MyException异常对象,名称为:ex_obj1:2

  结果不出所料,异常对象永远也只会被构造两次。所以异常对象“按引用传递”的方式,是综合性能最好的一种方式,效率既非常高(仅比“按指针传递”的方式多一次),同时也很安全和友善直观(这一点比“按指针传递”的方式好很多)。另外,这里也同样可以把“按引用传递”的方式和“按值传递”的方式相混合,代码示例如下:

  void main()

  {

  try

  {

  try

  {

  // 抛出一个异常对象

  throw MyException("ex_obj1");

  }

  // 这里按引用传递的方式

  catch(MyException& e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  cout<<"下面重新抛出异常"<<< p=""> <<>

  // 异常对象重新被抛出

  throw;

  }

  }

  // 这里按值传递的方式

  catch(MyException e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  }

  }

  3、最后,讨论异常对象“按指针传递”的方式下,异常对象的构造、传递和析构销毁的过程有何不同之处?其实这种方式不需要过多讨论,因为异常对象“按指针传递”的方式下,异常对象永远也只会需要被构造一次,实际上,它被传递只是一个32bit的指针值而已,不会涉及到异常对象的拷贝复制过程。但是有一点是需要注意的,那就是对异常对象的析构销毁必须要放在最后一个catch block处理完之后,中间层的catch block是决不应该delete掉这个一般在堆中分配的异常对象。

  catch block块处理异常时,又激发了另外一个异常的抛出

  呵呵!表面上看起来,这种情况下会很复杂,因为好像前面一个异常错误还没有被处理完,又引发了另外的一个异常错误,岂不是很麻烦呀!其实不然,系统对这种接力方式的异常重新抛出的处理策略往往很简单,那就是系统认为,当在catch block的代码执行过程中,如果抛出另一个异常,而导致控制流离开此catch block域,那么前一个异常会被认为处理完毕,并释放临时的异常对象,同时产生下一个异常的搜索catch block过程和异常处理的过程等。也即就是说,系统会把这种异常的重新抛出情况,认为是两次分离的异常。虽然它们是连在一起,并能够形成异常的接力抛出,但是处理上,它们完全是被分开进行的。所以说,这种情况下,往往会产生后一次异常对前一次异常的覆盖。

  另一种特殊的形式的异常被重新抛出

  前面我们所讨论的异常被重新抛出,它们都会导致控制流离开catch block模块,也即整个异常的接力处理过程是分层进行的。但实际上,异常的重新抛出后,是可以把它局限于当前的catch block域内,让它逃离不开这个作用域。也即通过在catch block再潜套一个try catch块,示例代码如下:

  void main()

  {

  try

  {

  try

  {

  // 动态在堆中构造的异常对象

  throw MyException("ex_obj1");

  }

  catch(MyException& e)

  {

  // 这里再潜套一个try catch块

  try

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  cout<<"下面重新抛出异常"<<< p=""> <<>

  //重新抛出异常

  throw;

  // 或者这样重新抛出异常

  throw e;

  }

  catch(MyException& ex)

  {

  }

  // 永远也逃不出我的魔掌

  catch(...)

  {

  }

  }

  }

  catch(MyException& e)

  {

  cout<<<"捕获到一个myexception*类型的异常,名称为:"<<< p=""> <<"捕获到一个myexception*类型的异常,名称为:"<<<>

  }

  }

  呵呵,通过上面的方式,可以让当前的catch block的处理得以安全保障,防止可能潜在的异常再次出现,而导致上层可能处理不了其它的意外异常,而引发程序崩溃。所以说,C++异常模型的确是非常灵活,功能也非常强大。

  总结

  (1) 异常重新的被抛出,它的处理过程虽然稍微略有些复杂,但是总体上还是比较易于理解的。它更不会影响并破坏前面章节中所讲述过的许多规则,它们的处理策略和思想是相一致的;

  (2) 再次强调,异常对象“按引用传递”的方式是综合性能最佳的方式。

  对C++中的异常处理机制的阐述,到此暂告一个段落,从下篇文章开始,主人公阿愚将继续开阔自身的视野,以对异常处理编程进入一个更加广泛的探讨之中。各位程序员朋友们,继续吧!


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GreenCppC 2008-8-24 ========================================= // I 类,对象,函数重载 //-------- From C to C++ ------------ // A simple C Program! // convert a string to uppercase! #include <stdio.h> #define N 200 int main(){ char ms[N]; int i; printf("Input ms: "); gets(ms); for(i=0;ms[i];i++) if(ms[i]>='a'&&ms[i]<='z') ms[i]-='\x20'; puts(ms); return 0; } /* path d:\wingw\bin gcc abc.c -o abc.exe */ //------------------------------ // A better C Program! #include <stdio.h> #define N 200 void strUpper(char *s); void strLower(char *s); int main(){ char ms[N]; printf("Input ms: "); gets(ms); strUpper(ms); puts(ms); strLower(ms); puts(ms); return 0; } void strUpper(char *s) { for(;*s;s++) if(*s>='a'&&*s<='z')*s-='\x20'; } void strLower(char *s) { for(;*s;s++) if(*s>='A'&&*s<='Z')*s+='\x20'; } //------------------------------ // A C++ Program without class and object! #include <iostream> using namespace std; const int N=200; void strUpper(char *s); void strLower(char *s); int main(){ char ms[N]; cout<<"Input ms: "; cin.getline(ms,N); strUpper(ms); cout<<ms<<"\n"; strLower(ms); cout<<ms<<endl; return 0; } void strUpper(char *s) { for(;*s;s++) if(*s>='a'&&*s<='z')*s-='\x20'; } void strLower(char *s) { for(;*s;s++) if(*s>='A'&&*s<='Z')*s+='\x20'; } //------------------------------ // A C++ Program with class and object! #include <iostream> using namespace std; const int N=200; class Str{ char s[N]; public: void out(){cout<<s<<"\n";} void in(){cout<<"s: "; cin.getline(s,N);} void upper(); void lower(); }; void Str::upper() {char *p; for(p=s;*p;p++) if(*p>='a'&&*p<='z')*p-='\x20'; } void Str::lower() { for(char *p=s;*p;p++) if(*p>='A'&&*p<='Z')*p+='\x20'; } // - - - int main(){ Str a; cin>>a.s; //error! a.in(); a.upper(); a.out(); a.lower(); a.out(); return 0; } ========================================= // II. 构造与析构函数 #include <iostream> #include <cstring> using namespace std; class child { char name[20]; int age; public: child(); child(char *n,int a); void ask(char *n); void ask(int a); ~child(); }; // --- child::child(){ strcpy(name,"Tomme"); age=3; } // --- child::child(char *name,int age){ strcpy(this->name,name); this->age=age; // this -- address of self object } // --- void child::ask(char *n){ if(!strcmp(name,n)) cout<<"Yes, i am "<<n<<".\n"; else cout<<"No, i am not "<<n<<".\n"; } // --- void child::ask(int age){ if(this->age==age) cout<<"Yes, i am "<<age<<" years old.\n"; else cout<<"No, i am not "<<age<<" years old.\n"; } // --- child::~child(){ cout<<name<<": Bye!\n"; } //---------- int main(){ child tom,rose("Rosie",4); tom.age=4; // error! tom.ask("tom"); rose.ask("Alice"); tom.ask(2); return 0; } ========================================= // III. 继承 #include <iostream> #include <cstring> using namespace std; class child { protected: //note this change! char name[20]; int age; public: child(); child(char *n,int a); void ask(char *n); void ask(int a); }; // --- child::child(){ strcpy(name,"Tomme"); age=3; } // --- child::child(char *n,int a){ strcpy(name,n); age=a; } // --- void child::ask(char *n){ if(!strcmp(name,n)) cout<<"Yes, i am "<<n<<".\n"; else cout<<"No, i am not "<<n<<".\n"; } // --- void child::ask(int a){ if(a==age) cout<<"Yes, i am "<<a<<" years old.\n"; else cout<<"No, i am not "<<a<<" years old.\n"; } // ----------- class pupil: public child{ char book[20]; public: pupil(char *n,int a,char *b); void list(); }; // --- pupil::pupil(char *n,int a,char *b): child(n,a) { strcpy(book,b); } // --- void pupil::list() { cout<<name<<" "<<age<<" "<<book<<"\n"; } //---------- int main(){ child tom,rose("Rosie",4); tom.ask("tom"); rose.ask("Alice"); tom.ask(2); pupil green("Green",9,"Nature"); green.ask("Jack"); green.ask(10); green.list(); return 0; } ========================================= // IV. 增加与基类成员函数同名的函数; // 调用基类成员函数;内联函数(in-line) #include <iostream> #include <cstring> using namespace std; class child { protected: char name[20]; int age; public: child(); child(char *n,int a); void ask(char *n); void ask(int a); }; // --- child::child(){ strcpy(name,"Tomme"); age=3; } // --- child::child(char *n,int a){ strcpy(name,n); age=a; } // --- void child::ask(char *n){ if(!strcmp(name,n)) cout<<"Yes, i am "<<n<<".\n"; else cout<<"No, i am not "<<n<<".\n"; } // --- void child::ask(int a){ if(a==age) cout<<"Yes, i am "<<a<<" years old.\n"; else cout<<"No, i am not "<<a<<" years old.\n"; } // ----------- class pupil: public child{ char book[20]; public: pupil(char *n,int a,char *b); void ask(char *n){child::ask(n);} // in-line! void ask(int a){child::ask(a);} void ask(); }; // --- pupil::pupil(char *n,int a,char *b): child(n,a) { strcpy(book,b); } // --- void pupil::ask() { cout<<name<<" "<<age<<" "<<book<<"\n"; } //---------- int main(){ child tom,rose("Rosie",4); tom.ask("tom"); rose.ask("Alice"); tom.ask(2); pupil green("Green",9,"Nature"); green.ask("Jack"); green.ask(10); green.ask(); return 0; } ===================================== // V. 对象数组 #include <iostream> using namespace std; class C1{ int a,b; public: C1(int m,int n){a=m;b=n;} int getAdd(){return a+b;} }; int main() {C1 ob[3]={C1(1,2),C1(3,4),C1(5,6)}; int i; for(i=0;i<3;i++) cout << ob[i].getAdd() << " "; // C1 oe[2]; -- Error! means constructor is C1() // -- need reload C1::C1(){ ... } // -- eg, C1::C1(){a=b=0;} return 0; } ========================================= // VI. 指向对象的指针 C1 obA(2,3), *p; p=&obA; cout << p->getAdd(); //------------- C1 ob[3]={C1(1,2),C1(3,4),C1(5,6)}, *q; q=ob; for(int i=0;i<3;i++) {cout << q->getAdd() << " "; q++; } //------------- class C2{ public: int a; C2(int k){a=k*k;} }; // . . . C2 obB(9); int *p; p=&obB.a; // Note! a is public, p to member cout << *p; // 指向派生类的指针 class Base{ public: int a,b; Base(int m,int n){a=m;b=n;} int getAdd(){return a+b;} }; class Derived: public Base{ int c; public: Derived(int x,int y,int z): Base(x,y) {c=z;} float getAve(){return (a+b+c)/3.0F;} }; // ... Base *bp; Derived d(6,7,9); bp=&d; cout << bp->getAdd(); cout << bp->getAve(); // Error! cout << ((Derived *) bp) ->getAve(); ========================================= // VII. 动态分配:new, delete #include <iostream> #include <new> using namespace std; int main() {int *p; try{ p=new int; } catch(bad_alloc ex){ cout << "New failed!\n"; return -1; } *p=20; cout << "At "<<p<<" is "<< *p <<"\n"; delete p; return 0; } //------------- #include <iostream> #include <new> using namespace std; int main() {int *p; try{ p=new int[6]; } catch(bad_alloc ex){ cout << "New failed!\n"; return -1; } float ave=0.0F; int i; cout<<"Enter numbers: "; for(i=0;i<6;i++) {cin>>p[i]; ave+= *(p+i); cout<< *(p+i); } ave/=6.0F; cout << "Ave = "<< ave <<"\n"; delete [] p; return 0; } //------------- #include <iostream> #include <cstring> #include <new> using namespace std; class Balance{ char name[40]; double curValue; public: Balance(char *n,double v){ strcpy(name,n); curValue=v; } void getValue(char *n,double &v){ strcpy(n,name); v=curValue; } }; int main() { Balance *p; char s[40]; double bal; try{ p=new Balance("Robin Hood",3536.45); } catch(bad_alloc ex){ cout << "New failed!\n"; return -1; } p->getValue(s,bal); cout<<s<<"\'s balance is "<<bal<<"\n"; delete p; return 0; } ========================================= // VIII. 传递引用 #include <iostream> using namespace std; void neg1(int k); void neg2(int *p); void neg3(int &k); int main() { int x=20; neg1(x); neg2(&x); cout << x<< "\n"; neg3(x); cout << x<< "\n"; } // - - - void neg1(int k) { k=-k;} void neg2(int *p) { *p=-*p;} void neg3(int &k) { k=-k; } //------------- #include <iostream> using namespace std; class C1{ public: int k; void neg(C1 &o){o.k=-o.k;} // -- no temp object created }; int main() { C1 ob; ob.k=20; ob.neg(ob); cout << ob.k <<"\n"; return 0; } //----------------------------------- /* Input a sentence,reverse all the words except other chars, eg: etihw, dna kcalb! => white, and black! NOT: !black and ,white */ #include <iostream> #include <new> #include <cstdlib> #include <cctype> using namespace std; const int N=200; /* - - - - - - */ class CharStack{ const int StkLen; char *data; int top; public: CharStack(); ~CharStack(){delete []data;} int push(char x) {if(top>=StkLen-1)return -1; // it's full top++; data[top]=x; return 0; } int pop(char &x) {if(top<=-1)return -1; // empty! x= *(data+top); top--; return 0; } }; CharStack::CharStack():StkLen(40) {try{ data=new char[StkLen]; }catch(bad_alloc){cout<<"New failed!"; exit(-1);} top=-1; } // --------- class WordRev{ char ms[N]; public: void reads() { cout<<"Input str:\n"; cin.getline(ms,N); } void prints() { cout<<ms<<"\n"; } void wRev(); }; void WordRev::wRev() {CharStack stk; int i=0,wStart,wEnd; while(ms[i]) {if(!isalpha(ms[i]))i++; else {wStart=i; while(isalpha(ms[i]))stk.push(ms[i++]); wEnd=i; i=wStart; while(!stk.pop(ms[i]))i++; i=wEnd; } } } // --------- int main() { WordRev sr; sr.reads(); sr.wRev(); sr.prints(); return 0; } ========================================= // IX 函数形参使用默认值 #include <iostream> #include <cstring> using namespace std; // ----------- class pupil{ public: char name[20]; int age; char book[20]; pupil(char *n,int a,char *b); void list(); }; // --- pupil::pupil(char *n,int a,char *b){ strcpy(name,n); age=a; strcpy(book,b); } // --- void pupil::list() { cout<<name<<" "<<age<<" "<<book<<"\n"; } //---------- void nextYear(pupil &c,char *book="Math"); //---------- int main(){ pupil green("Green",9,"Chinese"); green.list(); nextYear(green); green.list(); nextYear(green,"Nature"); green.list(); return 0; } //---------- void nextYear(pupil &c,char *book="Math") { // ="Math" Should be omitted, for previous prototype c.age++; strcpy(c.book,book); return; } /* D:\green>g++ pupil.cpp -o pupil.exe pupil.cpp: In function `void nextYear(pupil&, char*)': pupil.cpp:41: error: default argument given for parameter 2 of `void nextYear(pupil&, char*)' pupil.cpp:25: error: after previous specification in `void nextYear(pupil&, char*)' */ ========================================= // X. 虚函数 virtual #include <iostream> using namespace std; class base{ public: virtual void vf(){cout<<"base's vf.\n";} }; class derived1:public base{ public: virtual void vf(){cout<<"derived1's vf.\n";} }; class derived2:public base{ public: virtual void vf(){cout<<"derived2's vf.\n";} }; void f(base &r){r.vf();} int main() {base b, *p; derived1 d1; derived2 d2; b.vf(); d1.vf(); d2.vf(); p=&b; p->vf(); p=&d1; p->vf(); // derived1's vf. p=&d2; p->vf(); // derived2's vf. f(b); f(d1); // derived1's vf. f(d2); // derived2's vf. return 0; } ========================================= // XI. 对象赋值问题 #include <iostream> #include <cstdlib> #include <new> using namespace std; class Myclass{ int *p; public: Myclass(int i); void show(){cout<< *p<<"\n";} ~Myclass(){delete p;} }; Myclass::Myclass(int i){ try{ p=new int; } catch(bad_alloc e) {cout<< "New failed!\n"; exit(-1); } *p=i; } int main() {Myclass a(20); Myclass b=a; //copy by bits b.show(); return 0; // 错误!对象中 p 所指向的内存空间将被释放 2 次! } ========================================= // XII. 拷贝构造函数 ---- 解决对象参数传递的副作用问题 #include <iostream> #include <new> using namespace std; class array{ public: int *p; int size; array( ){p=NULL;size=0; }; array(int sz); array(const array &a); ~array(){if(!p){delete [ ]p; size=0;}} void input(); }; array::array(int sz){ size=sz; try{ p=new int[size]; }catch (bad_alloc xa){ cout <<"Alloc failed!"; exit(EXIT_FAILURE); } } array::array(const array &a){ try{ p=new int[a.size]; }catch (bad_alloc xa){ cout <<"Alloc failed!"; exit(EXIT_FAILURE); } size=a.size; for(int i=0;i<size;i++)p[i]=a.p[i]; } void array::input(){ cout<<"Input "<<size<<" integers: "; for(int i=0;i<size;i++)cin>>p[i]; } void inc(array a){ int i; for(i=0;i<a.size;i++) if(a.p[i]==59)a.p[i]++; cout<<"Result is: " ; for(i=0;i<a.size;i++)cout<<a.p[i]<<' '; cout<<"\n"; } int main(){ array a(4),c; a.input(); array b(a); // 调用拷贝构造函数 inc(b); //调用拷贝构造函数(隐式) for(int i=0;i<b.size;i++)cout<<b.p[i]<<' '; return 0; } ========================================= // XIII. 运算符重载 #include <iostream> using namespace std; class loc{ int longitude,latitude; public: loc(){} //needed to construct temp objects loc(int lg,int lt){longitude=lg; latitude=lt;} void show(){cout<<longitude<<" "<<latitude<<"\n";} loc operator+(loc op2); loc operator++(); loc operator=(loc op2); loc operator+=(loc op2); }; loc loc::operator+(loc op2) {loc temp; temp.longitude=op2.longitude+longitude; temp.latitude=op2.latitude+latitude; return temp; } loc loc::operator++() //前缀形式 prefix {longitude++; latitude++; return *this; } loc loc::operator=(loc op2) { longitude=op2.longitude; latitude=op2.latitude; return *this; //为连续赋值 } loc loc::operator+=(loc op2) { longigude=op2.longitude+longitude; latitude=op2.latitude+latitude; return *this; //为连续赋值 } // operator=和operator++等都改变了对象的值 int main() {loc ob1(10,20),ob2(5,30),ob3; ob3=ob1+ob2; ++ob3; ob3.show(); ob1+=ob2; ob1=ob1+ob2; // ob1.+(ob2) like (ob1+ob2).show(); ++ob1; ob1=ob2=ob3; ... ... } ========================================= // XIV. 异常的抛出,捕获与处理 #include <iostream> using namespace std; int main() {cout <<"Start\n"; try { cout << "Inside try block\n"; throw 100; cout << "This will not execute"; } catch (int i) { cout << "Caught an exception, value is: "; cout << i <<"\n"; } cout << "End\n"; return 0; } // -------------------- #include <iostream> using namespace std; void xtest(int test) { cout << "Inside xtest!\n"; if(test) throw test; } int main() {cout <<"Start\n"; try { cout << "Inside try block\n"; xtest(0); xtest(1); xtest(2); } catch (int i) { cout << "Caught an exception, value is: "; cout << i <<"\n"; } cout << "End\n"; return 0; } // 捕获异常类 #include <iostream> #include <cstring> using namespace std; class MyException{ public: char how[80]; int what; MyException(){*how=0; what=0;} MyException(char *s, int n) {strcpy(how,s); what=n; } }; int main() {int i; try { cout <<"Enter a positive number: "; cin >> i; if(i<0) throw MyException("Not Positive",i); } catch (MyException e) { cout << e.how<<": "; cout << e.what <<"\n"; } return 0; } // 捕获派生异常类 #include <iostream> using namespace std; class B { }; class D: public B { }; int main() {D derived; try { throw derived; } catch (D d) { cout << "Caught a derived class, not the base! \n"; } catch (B b) { cout << "Caught the base class! \n"; } return 0; } // 异常的限制及捕获所有异常 #include <iostream> using namespace std; void xtest(int test) throw (int,char,double,char *) { try { if(test==0) throw test; if(test==1) throw 'a'; if(test==2) throw 12.34; if(test==3) throw "A string."; } catch (int i) { cout << "Caught an integer!"; } catch (...) { cout << "Caught Another!"; } } int main() {cout <<"Start\n"; xtest(0); xtest(1); xtest(2); xtest(3); cout << "End\n"; return 0; } // 异常的再次抛出 #include <iostream> using namespace std; void xhandler() { try { throw "Hello!"; } catch (const char *) { cout << "Caught a string inside!\n"; throw; //rethrow char * out of function } } int main() {cout <<"Start\n"; try { xhandler(); } catch (const char *) { cout << "Caught a string outside!\n"; } cout << "End\n"; return 0; } // 一个简单的程序 #include <iostream> using namespace std; int main() {int a,b; cout <<"Enter a b: "; cin >> a >> b; try { if(!b) throw b; cout << "Result: "<< a/b << endl; } catch (int i) { cout << "Can't divide by zero!\n"; } return 0; } ========================================= XV. 模板 //模板之通用函数 #include <iostream> using namespace std; template <class X> void superSwap(X &a, X &b) { X t; t=a; a=b; b=t; }; int main() { int m=10, n=20; double x=10.1, y=20.2; char a='A', b='\x42'; superSwap(m,n); superSwap(x,y); superSwap(a,b); cout<<"m="<<m<<", n="<<n<<'\n'; cout<<"x="<<x<<", y="<<y<<'\n'; cout<<"a="<<a<<", b="<<b<<'\n'; return 0; } ---------------------------------- #include <iostream> using namespace std; template <class type1,class type2> void myfunc(type1 x, type2 y) { cout<<x<<' '<<y<<'\n'; } int main() { myfunc("C++ is great!", 100); // char* , int myfunc(1234.56, 20L); // double , long int return 0; } // 模板之通用类 #include <iostream> using namespace std; const int Size=20; template <class DataType> class SuperStack{ DataType data[Size]; int top; public: SuperStack(){top= -1;} int push(DataType x); int pop(DataType &x); }; template <class DataType> int SuperStack<DataType>::push(DataType x) { if(top>=Size-1) return -1; // stack is full data[++top]=x; return 0; } template <class DataType> int SuperStack<DataType>::pop(DataType &x) { if(top<0) return -1; // stack is empty x=data[top--]; return 0; } int main() { SuperStack<char> chStack; SuperStack<double> dbStack; char s[Size]="ABC"; double a[Size]={20.1, 21.2, 22.3}; int i; for(i=0;i<3;i++) { chStack.push(s[i]); dbStack.push(a[i]); } for(i=0;i<3;i++) { chStack.pop(s[i]); dbStack.pop(a[i]); } cout << s << '\n'; for(i=0;i<3;i++) cout << a[i]<<' '; cout<<'\n'; return 0; } ========================================= XVI. 名字空间 #include <iostream> using namespace std; namespace GreenNamespace{ char Say[80]="TRUTH, Must thou Know!"; bool isUpperLetter(char ch) { if(ch>='A' &&ch<='Z')return true; return false; } class X{ public: int year; X(int y){year=y;} }; // note this semi-colon! } using namespace GreenNamespace; int main() {cout << Say <<'\n'; char *p=Say; int count=0; for(;*p;p++) if(isUpperLetter(*p))count++; cout << "count = " << count <<'\n'; X ob(2006); cout << ob.year <<"\n"; return 0; } //------------------------ ////////// OneCount.cpp//////// namespace BitsSpace{ int onePerByte(char x); int oneCount(char *buf,int bytes); // ------------------- int onePerByte(char x) {int count=0,i; for(i=0;i<8;i++) {if(x & '\x1') count++; // 'A'<=> '\x41' x>>=1; //x=x>>1; } return count; } // --- int oneCount(char *buf,int bytes) {int count=0,i; for(i=0;i<bytes;i++) count+=onePerByte(buf[i]); return count; } // --- } ---------------- ////////// testOne.cpp//////// #include <iostream> #include "OneCount.cpp" using namespace std; //using namespace BitsSpace; const int SLen=80; // ---- int main(){ char ms[SLen],*p; cout<<"Enter your words:\n> " ; cin.getline(ms,SLen); cout<< BitsSpace::oneCount(ms,strlen(ms))<<"\n"; cout<<"Size of char: "<<sizeof(char)<<"\n"; return 0; } ========================================= //////////////////////////////// // XVII. C++ file handle, 6 programs // by Hs.li 2007.4.28 //////////////////////////////// // Write text strings! #include <iostream> #include <fstream> using namespace std; #define MaxN 200 // - - - - - - int main() {fstream outF; char text[MaxN]; cout<<"Enter lines, end with empty line:\n"; outF.open("str.txt",ios::out); if(!outF.is_open()) {cout<<"File open failed!\n"; return -1; } while(1) { cin.getline(text,MaxN); if(!text[0])break; outF<<text<<"\n"; if(outF.bad()||outF.fail()) {cout<<"Write file error!\n"; outF.close(); return -2; } } cout<<"Write text file okey!\n"; outF.close(); return 0; } ========================= // Read text strings! #include <iostream> #include <fstream> using namespace std; #define MaxN 200 // - - - - - - int main() {fstream inF; char text[MaxN]; cout<<"The text lines are:\n"; inF.open("str.txt",ios::in); if(!inF.is_open()) {cout<<"File open failed!\n"; return -1; } while(1) { inF.getline(text,MaxN); if(inF.eof()|inF.fail()|inF.bad())break; cout<<text<<"\n"; } if(!inF.eof()) {cout<<"Read file error!\n"; inF.close(); return -2; } cout<<"---- End!\n"; inF.close(); return 0; } ====================== // Write text data! #include <iostream> #include <fstream> using namespace std; // - - - - - - int main() { const int MaxN=8; fstream outF; double a[MaxN]; char *fname="data.txt"; int i; cout<<"Please input 8 double: "; for(i=0;i<MaxN;i++) cin>>a[i]; outF.open(fname,ios::out); if(!outF.is_open()) {cout<<"File open failed!\n"; return -1; } outF.exceptions(fstream::failbit| fstream::badbit); try{ for(i=0;i<MaxN;i++) outF<<a[i]<<" "; } catch(std::exception &e) {cout<<"Exception caught:"<<e.what()<<endl; outF.close(); return -2; } outF.close(); cout<<"Text data file write okey!\n"; return 0; } ============================ // Read text data! #include <iostream> #include <fstream> using namespace std; // - - - - - - int main() { fstream inF; double b; char *fname="data.txt"; cout<<"Please wait for reading data:\n"; inF.open(fname,ios::in); if(!inF.is_open()) {cout<<"File open failed!\n"; return -1; } inF.exceptions(fstream::failbit| fstream::badbit|fstream::eofbit); try{ while(1) { inF>>b; cout<<b<<" "; } }catch(std::exception &e) { if(!inF.eof()) { cout<<"Exception caught:"<<e.what()<<endl; inF.close(); return -2; } } inF.close(); cout<<"\nText data file read okey!\n"; return 0; } =============================== // Write binary data! #include <iostream> #include <fstream> using namespace std; // - - - - - - int main() { const int MaxN=8; fstream outF; double a[MaxN]; char *fname="data.dat"; int i; cout<<"Please input 8 double: "; for(i=0;i<MaxN;i++) cin>>a[i]; outF.open(fname,ios::out|ios::binary); if(!outF.is_open()) {cout<<"File open failed!\n"; return -1; } outF.exceptions(fstream::failbit| fstream::badbit); try{ outF.write((char *)a,MaxN*sizeof(double)); } catch(std::exception &e) {cout<<"Exception caught:"<<e.what()<<endl; outF.close(); return -2; } outF.close(); cout<<"Binary data file write okey!\n"; return 0; } ================================ // Read binary data! #include <iostream> #include <fstream> using namespace std; // - - - - - - int main() { fstream inF; double b; char *fname="data.dat"; cout<<"Please wait for reading data:\n"; inF.open(fname,ios::in|ios::binary); if(!inF.is_open()) {cout<<"File open failed!\n"; return -1; } inF.exceptions(fstream::failbit| fstream::badbit|fstream::eofbit); try{ while(1) { inF.read((char *)&b,sizeof(double)); cout<<b<<" "; } }catch(std::exception &e) { if(!inF.eof()) { cout<<"Exception caught:"<<e.what()<<endl; inF.close(); return -2; } } inF.close(); cout<<"\nBinary data file read okey!\n"; return 0; } ////////////////////////////////////////////////

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值