当我们throw出类对象时,使用catch捕获异常时有三种选择,分别是捕获对象元素、捕获引用和捕获指针,那么这三种情况下,捕获到的变量是如何分配内存,他的生命周期又是如何呢,首先结论如下:
- 捕获类对象的元素:调用拷贝构造函数把抛出的对象元素拷贝给catch的参数对象元素,调用拷贝构造函数;
- 捕获类对象的引用:catch语句中的对象直接使用抛出的对象;
- 捕获类对象的指针:需要手动new和delete控制内存;
结论如上,下面通过一个程序详细探究(提示:因为catch严格按照类型匹配进行接异常,所以catch元素和catch引用不能同时出现)。
1. #include <iostream> 2. using namespace std; 3. 4. class pIsNULL 5. { 6. public: 7. pIsNULL() 8. { 9. cout << "pIsNULL 无参构造函数" << endl; 10. } 11. //pIsNULL(pIsNULL& p) 12. //错误 C2440 “throw” : 无法从“pIsNULL”转换为“pIsNULL” 13. //错误(活动) E0334 类 "pIsNULL" 没有适当的复制构造函数 14. pIsNULL(const pIsNULL& p) //拷贝构造函数要加 const 15. { 16. cout << "pIsNULL 拷贝构造函数" << endl; 17. } 18. ~pIsNULL() 19. { 20. cout << "pIsNULL 析构函数" << endl; 21. } 22. public: 23. void print_err_type() 24. { 25. cout << "异常原因:指针指向NULL" << endl; 26. } 27. }; 28. 29. void print_str(char* str) 30. { 31. if (str == NULL) 32. { 33. throw pIsNULL(); //调用无参构造函数 34. } 35. cout << str << endl; 36. } 37. 38. void TestFunc1() 39. { 40. char buf1[] = "hello"; 41. char* buf2 = NULL; 42. 43. try 44. { 45. print_str(buf2); 46. } 47. catch (pIsNULL e) //调用拷贝构造函数,将 throw 出的对象复制给 e 48. { 49. e.print_err_type(); 50. } 51. catch (...) 52. { 53. cout << "未知异常" << endl; 54. } 55. } 56. 57. void TestFunc2() 58. { 59. char buf1[] = "hello"; 60. char* buf2 = NULL; 61. 62. try 63. { 64. print_str(buf2); 65. } 66. catch (pIsNULL& e) //不会调用拷贝构造函数 67. { 68. e.print_err_type(); 69. } 70. catch (...) 71. { 72. cout << "未知异常" << endl; 73. } 74. } 75. 76. void print_str2(char* str) 77. { 78. if (str == NULL) 79. { 80. throw new pIsNULL; 81. } 82. cout << str << endl; 83. } 84. 85. void TestFunc3() 86. { 87. char buf1[] = "hello"; 88. char* buf2 = NULL; 89. 90. try 91. { 92. print_str2(buf2); 93. } 94. catch (pIsNULL* e) 95. { 96. e->print_err_type(); 97. delete e; 98. } 99. catch (...) 100. { 101. cout << "未知异常" << endl; 102. } 103. } 104. 105. int main() 106. { 107. TestFunc1(); //用对象元素接异常 108. //TestFunc2(); //用引用接异常 109. //TestFunc3(); //用指针接 110. 111. system("pause"); 112. return 0; 113. }
分别在主函数中调用三个测试函数,观察打印结果:
①在主函数中调用第一个测试函数,用元素捕获异常
TestFunc1(); //用对象元素接异常
打印结果如下
可以看到,在catch的时候会将throw处构造的对象通过拷贝构造函数复制给catch语句中的元素e,因为这里一共有两个对象,所以在异常结束时会调用两次析构函数,分别析构两个对象。
②在主函数调用第二个测试函数,用引用捕获异常
TestFunc2(); //用引用接异常
运行结果如下
使用引用捕获异常的时候会直接使用throw处构造的对象,所以不会调用拷贝构造函数,只调用一次析构函数。
③在主函数调用第三个测试函数,用指针捕获异常
TestFunc3(); //用指针接
抛出指针类型的异常最好手动new和delete来管理内存。