Files
张宗平 13b3ebbe14 课程代码仓库初始化:Part1 (144) + Part3 (29) 全量生成与验证
把川大 wiki「02.Qt方向」课程代码整理为 CMake 管理的可编译运行项目。

Part1 C/C++ 强化 (p01/, 144 目标, host gcc/g++ 构建, 全绿):
- 修复全部编译失败:补 <cstring>/<fstream>/<iomanip>/<vector> 等缺失头
  (common_fix + infer_stl_includes 自动检测注入)、修正 pause() 壳注入
  (改检测已转换后的 pause 调用)、namespace 提升出函数体、const 成员函数
  正确性、dedupe 不再破坏函数重载集、K&R 隐式 int / 动态异常规范等
  C++17 移除特性的可编译等价改写。
- 目录采用深缩写 p01/ch04/s03/ (原 part1_cpp/ch04_class_object/03_ctor_dtor)。

Part3 Qt 桌面 (p03/, 29 目标, 隔离 Qt 5.14.2 构建, 全绿):
- gen_part3.py: Qt 外壳模板 (main+QApplication+show, QTimer 自动退出便于
  offscreen 验证)、AUTOMOC + .moc include、rewrite_* 处理散文混入/重载/
  资源依赖、生成占位 png/gif + .qrc + rundata 样例数据。
- 修正 QLable 笔误、全角分号、.→->、Windows 路径、QApplication 阻塞事件
  循环改为 QCoreApplication 等。

任务1 概念/版本验证:
- tools/std_probe.py: 跨 -std= 编译探针 (--pedantic-errors 让已废除特性硬失败)。
- tools/demo_versions/: K&R 隐式 int、三目左值、void* 转换、enum 赋整、
  const 经指针、动态异常规范 共 6 个跨版本演示。
- docs/VERSION_NOTES.md: 每条「原文说法→C17/C++17 是否成立→何版本成立→已验证」。

文档: 顶层 README、docs/SOURCE_PAGES.md (页面→目录映射)、
docs/ERRATA.md (Part1 修正)、docs/ERRATA_part3.md (Part3 修正)。
另修复 tools/run.sh / run_qt.sh 的 ${1:?...} 引号语法 bug。

隔离 Qt 验证: ldd 0 系统 Qt 引用; 173/173 目标全绿; p03 offscreen 运行通过。
2026-06-30 14:16:55 +08:00

1 line
21 KiB
JSON
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"id":"58954474","type":"page","status":"current","title":"07.C++异常","version":{"by":{"type":"known","username":"ethanliu","userKey":"2c90931b6cfb654f016d5c69becc0001","profilePicture":{"path":"/images/icons/profilepics/default.svg","width":48,"height":48,"isDefault":true},"displayName":"刘浩","_links":{"self":"https://wiki.suncaper.net/rest/api/user?key=2c90931b6cfb654f016d5c69becc0001"},"_expandable":{"status":""}},"when":"2022-07-09T01:51:24.000Z","message":"","number":1,"minorEdit":false,"hidden":false,"_links":{"self":"https://wiki.suncaper.net/rest/experimental/content/58954474/version/1"},"_expandable":{"content":"/rest/api/content/58954474"}},"body":{"storage":{"value":"<ac:layout><ac:layout-section ac:type=\"two_right_sidebar\"><ac:layout-cell><h2><span>7.1 </span>异常基本概念</h2><p>Bjarne Stroustrup说:提供异常的基本目的就是为了处理上面的问题。基本思想是:让一个函数在发现了自己无法处理的错误时抛出(<span>throw</span>)一个异常,然后它的(直接或者间接)调用者能够处理这个问题。也就是《<span>C++ primer</span>》中说的:将问题检测和问题处理相分离。</p><p>一种思想:在所有支持异常处理的编程语言中(例如<span>java</span>),要认识到的一个思想:在异常处理过程中,由问题检测代码可以抛出一个对象给问题处理代码,通过这个对象的类型和内容,实际上完成了两个部分的通信,通信的内容是“出现了什么错误”。当然,各种语言对异常的具体实现有着或多或少的区别,但是这个通信的思想是不变的。</p><p><br /></p><table><colgroup><col /></colgroup><tbody><tr><td><p><strong>一句话:异常处理就是处理程序中的错误。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除<span>0</span>溢出,数组下标越界,所要读取的文件不存在<span>,</span>空指针,内存不足等等)。</strong></p></td></tr></tbody></table><p><br /></p><p><strong>回顾一下:我们以前编写程序是如何处理异常?</strong></p><p>在<span>C</span>语言的世界中,对错误的处理总是围绕着两种方法:一是使用整型的返回值标识错误;二是使用<span>errno</span>宏(可以简单的理解为一个全局整型变量)去记录错误。当然<span>C++</span>中仍然是可以用这两种方法的。</p><p>        这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回<span>1</span>表示成功,返回<span>0</span>表示出错;而有些函数返回<span>0</span>表示成功,返回非<span>0</span>表示出错。<span>  </span></p><p>        还有一个缺点就是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。当然,你也可以通过指针或者<span>C++</span>的引用来返回另外的值,但是这样可能会令你的程序略微晦涩难懂。</p><p><br /></p><p><strong>c++</strong><strong>异常机制相比<span>C</span>语言异常处理的优势<span>?</span></strong></p><ul><li>函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用<span>C</span>语言的<span>error</span>宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。</li><li>整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。</li><li>整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。</li><li>异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。</li></ul><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"e84dccb7-8032-45df-b117-9bc03d12cf8e\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[//如果判断返回值,那么返回值是错误码还是结果?\n//如果不判断返回值,那么b==0时候,程序结果已经不正确\n//A写的代码\nint A_MyDivide(int a,int b){\n\tif (b == 0){\n\t\treturn -1;\n\t}\n\n\treturn a / b;\n}\n\n//B写的代码\nint B_MyDivide(int a,int b){\n\n\tint ba = a + 100;\n\tint bb = b;\n\n\tint ret = A_MyDivide(ba, bb); //由于B没有处理异常,导致B结果运算错误\n\n\treturn ret;\n}\n\n//C写的代码\nint C_MyDivide(){\n\n\tint a = 10;\n\tint b = 0;\n\n\tint ret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常\n\tif (ret == -1){\n\t\treturn -1;\n\t}\n\telse{\n\t\treturn ret;\n\t}\n}\n\n//所以,我们希望:\n//1.异常应该捕获,如果你捕获,可以,那么异常必须继续抛给上层函数,你不处理,不代表你的上层不处理\n//2.这个例子,异常没有捕获的结果就是运行结果错的一塌糊涂,结果未知,未知的结果程序没有必要执行下去]]></ac:plain-text-body></ac:structured-macro><h2><span>7.2 </span>异常语法</h2><h3><span>7.2.1 </span>异常基本语法</h3><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"aee5511b-fd02-4834-b8ce-1061aa69489f\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[int A_MyDivide(int a, int b){\n\tif (b == 0){\n\t\tthrow 0;\n\t}\n\n\treturn a / b;\n}\n\n//B写的代码 B写代码比较粗心,忘记处理异常\nint B_MyDivide(int a, int b){\n\n\tint ba = a;\n\tint bb = b;\n\n\tint ret = A_MyDivide(ba, bb) + 100; //由于B没有处理异常,导致B结果运算错误\n\n\treturn ret;\n}\n\n//C写的代码\nint C_MyDivide(){\n\n\tint a = 10;\n\tint b = 0;\n\n\tint ret = 0;\n\n//没有处理异常,程序直接中断执行\n#if 1 \n\tret = B_MyDivide(a, b);\n\n//处理异常\n#else \n\ttry{\n\t\tret = B_MyDivide(a, b); //更严重的是,由于B没有继续抛出异常,导致C的代码没有办法捕获异常\n\t}\n\tcatch (int e){\n\t\tcout << \"C_MyDivide Call B_MyDivide 除数为:\" << e << endl;\n\t}\n#endif\n\t\n\treturn ret;\n}\n\nint main(){\n\n\tC_MyDivide();\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><p><strong>总结<span>:</span></strong></p><ul><li>若有异常则通过<span>throw</span>操作创建一个异常对象并抛出。</li><li>将可能抛出异常的程序段放到<span>try</span>块之中。</li><li>如果在<span>try</span>段执行期间没有引起异常,那么跟在<span>try</span>后面的<span>catch</span>字句就不会执行。</li><li>catch子句会根据出现的先后顺序被检查,匹配的<span>catch</span>语句捕获并处理异常<span>(</span>或继续抛出异常<span>)</span></li><li>如果匹配的处理未找到,则运行函数<span>terminate</span>将自动被调用,其缺省功能调用<span>abort</span>终止程序。</li><li>处理不了的异常,可以在<span>catch</span>的最后一个分支,使用<span>throw</span>,向上抛。</li></ul><p><span> </span></p><p>c++异常处理使得异常的引发和异常的处理不必在一个函数中,这样底层的函数可以着重解决具体问题,而不必过多的考虑异常的处理。上层调用者可以在适当的位置设计对不同类型异常的处理。</p><h3><span>7.2.2 </span>异常严格类型匹配</h3><p>异常机制和函数机制互不干涉<span>,</span>但是<strong>捕捉方式是通过严格类型匹配</strong>。</p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"8b51da98-bda5-4f5e-8a17-256253ccb148\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[void TestFunction(){\n\t\n\tcout << \"开始抛出异常...\" << endl;\n\t//throw 10; //抛出int类型异常\n\t//throw 'a'; //抛出char类型异常\n\t//throw \"abcd\"; //抛出char*类型异常\n\tstring ex = \"string exception!\";\n\tthrow ex;\n\n}\n\nint main(){\n\n\ttry{\n\t\tTestFunction();\n\t}\n\tcatch (int){\n\t\tcout << \"抛出Int类型异常!\" << endl;\n\t}\n\tcatch (char){\n\t\tcout << \"抛出Char类型异常!\" << endl;\n\t}\n\tcatch (char*){\n\t\tcout << \"抛出Char*类型异常!\" << endl;\n\t}\n\tcatch (string){\n\t\tcout << \"抛出string类型异常!\" << endl;\n\t}\n\t//捕获所有异常\n\tcatch (...){\n\t\tcout << \"抛出其他类型异常!\" << endl;\n\t}\n\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h3><span>7.2.3 </span>栈解旋<span>(unwinding)</span></h3><p>异常被抛出后,从进入<span>try</span>块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋<span>(unwinding).</span></p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"dce8d11f-a1f0-410c-a1a7-5659fcf7e134\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[class Person{\npublic:\n\tPerson(string name){\n\t\tmName = name;\n\t\tcout << mName << \"对象被创建!\" << endl;\n\t}\n\t~Person(){\n\t\tcout << mName << \"对象被析构!\" << endl;\n\t}\npublic:\n\tstring mName;\n};\n\nvoid TestFunction(){\n\t\n\tPerson p1(\"aaa\");\n\tPerson p2(\"bbb\");\n\tPerson p3(\"ccc\");\n\n\t//抛出异常\n\tthrow 10;\n}\n\nint main(){\n\n\ttry{\n\t\tTestFunction();\n\t}\n\tcatch (...){\n\t\tcout << \"异常被捕获!\" << endl;\n\t}\n\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h3><span>7.2.4 </span>异常接口声明</h3><ul><li>为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:<span>void func() throw(A,B,C);</span>这个函数<span>func</span>能够且只能抛出类型<span>A,B,C</span>及其子类型的异常。</li><li>如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如<span>:void func()</span></li><li>一个不抛任何类型异常的函数可声明为<span>:void func() throw()</span></li><li>如果一个函数抛出了它的异常接口声明所不允许抛出的异常<span>,unexcepted</span>函数会被调用,该函数默认行为调用<span>terminate</span>函数中断程序。</li></ul><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"9f01aa9a-d0d0-4a52-9c5f-eee9eb99d91c\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[//可抛出所有类型异常\nvoid TestFunction01(){\n\tthrow 10;\n}\n\n//只能抛出int char char*类型异常\nvoid TestFunction02() throw(int,char,char*){\n\tstring exception = \"error!\";\n\tthrow exception;\n}\n\n//不能抛出任何类型异常\nvoid TestFunction03() throw(){\n\tthrow 10;\n}\n\nint main(){\n\n\ttry{\n\t\t//TestFunction01();\n\t\t//TestFunction02();\n\t\t//TestFunction03();\n\t}\n\tcatch (...){\n\t\tcout << \"捕获异常!\" << endl;\n\t}\n\t\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><p>请分别在<span>qt vs linux</span>下做测试<span>! Qt and Linux </span>正确<span>!</span></p><h3><span>7.2.5 </span>异常变量生命周期</h3><ul><li>throw的异常是有类型的,可以是数字、字符串、类对象。</li><li>throw的异常是有类型的,<span>catch</span>需严格匹配异常类型。</li></ul><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"853be07a-a0e7-4770-b950-23efe59db199\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[class MyException\n{\npublic:\n\tMyException(){\n\t\tcout << \"异常变量构造\" << endl;\n\t};\n\tMyException(const MyException & e)\n\t{\n\t\tcout << \"拷贝构造\" << endl;\n\t}\n\t~MyException()\n\t{\n\t\tcout << \"异常变量析构\" << endl;\n\t}\n};\nvoid DoWork()\n{\n\tthrow new MyException(); //test1 2都用 throw MyExecption();\n}\n\nvoid test01()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t}\n}\nvoid test02()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException &e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t}\n}\n\nvoid test03()\n{\n\ttry\n\t{\n\t\tDoWork();\n\t}\n\tcatch (MyException *e)\n\t{\n\t\tcout << \"捕获 异常\" << endl;\n\t\tdelete e;\n\t}\n}]]></ac:plain-text-body></ac:structured-macro><h3>7.2.6 异常的多态使用</h3><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"5e6e913c-fa9c-4c1d-aadf-612f068d7cf2\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[//异常基类\nclass BaseException{\npublic:\n\tvirtual void printError(){};\n};\n\n//空指针异常\nclass NullPointerException : public BaseException{\npublic:\n\tvirtual void printError(){\n\t\tcout << \"空指针异常!\" << endl;\n\t}\n};\n//越界异常\nclass OutOfRangeException : public BaseException{\npublic:\n\tvirtual void printError(){\n\t\tcout << \"越界异常!\" << endl;\n\t}\n};\n\nvoid doWork(){\n\n\tthrow NullPointerException();\n}\n\nvoid test()\n{\n\ttry{\n\t\tdoWork();\n\t}\n\tcatch (BaseException& ex){\n\t\tex.printError();\n\t}\n}\n\n]]></ac:plain-text-body></ac:structured-macro><h2><span>7.3 C++</span>标准异常库</h2><h3><span>7.3.1 </span>标准库介绍</h3><p>标准库中也提供了很多的异常类,它们是通过类继承组织起来的。异常类继承层级结构图如下:</p><p><ac:image ac:height=\"400\"><ri:attachment ri:filename=\"image2021-5-14_14-34-14.png\" /></ac:image></p><p>每个类所在的头文件在图下方标识出来。</p><p><strong>标准异常类的成员:</strong></p><p>① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。</p><p>②<span> logic_error</span>类及其子类、<span>runtime_error</span>类及其子类,它们的构造函数是接受一个<span>string</span>类型的形式参数,用于异常信息的描述</p><p>③ 所有的异常类都有一个<span>what()</span>方法,返回<span>const char* </span>类型(<span>C</span>风格字符串)的值,描述异常信息。</p><p><br /></p><p><strong>标准异常类的具体描述:</strong></p><table><colgroup><col /><col /></colgroup><tbody><tr><td><p>异常名称</p></td><td><p>描述</p></td></tr><tr><td><p>exception</p></td><td><p>所有标准异常类的父类</p></td></tr><tr><td><p>bad_alloc</p></td><td><p>当<span>operator new and operator new[]</span>,请求分配内存失败时</p></td></tr><tr><td><p>bad_exception</p></td><td><p>这是个特殊的异常,如果函数的异常抛出列表里声明了<span>bad_exception</span>异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的<span>unexpected</span>函数中若抛出异常,不论什么类型,都会被替换为<span>bad_exception</span>类型</p></td></tr><tr><td><p>bad_typeid</p></td><td><p>使用<span>typeid</span>操作符,操作一个<span>NULL</span>指针,而该指针是带有虚函数的类,这时抛出<span>bad_typeid</span>异常</p></td></tr><tr><td><p>bad_cast</p></td><td><p>使用<span>dynamic_cast</span>转换引用失败的时候</p></td></tr><tr><td><p>ios_base::failure</p></td><td><p>io操作过程出现错误</p></td></tr><tr><td><p>logic_error</p></td><td><p>逻辑错误,可以在运行前检测的错误</p></td></tr><tr><td><p>runtime_error    </p></td><td><p>运行时错误,仅在运行时才可以检测的错误</p></td></tr></tbody></table><p><strong>logic_error</strong><strong>的子类:</strong></p><table><colgroup><col /><col /></colgroup><tbody><tr><td><p>异常名称</p></td><td><p>描述</p></td></tr><tr><td><p>length_error</p></td><td><p>试图生成一个超出该类型最大长度的对象时,例如<span>vector</span>的<span>resize</span>操作</p></td></tr><tr><td><p>domain_error</p></td><td><p>参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数</p></td></tr><tr><td><p>out_of_range</p></td><td><p>超出有效范围</p><p><br /></p></td></tr><tr><td><p>invalid_argument</p></td><td><p>参数不合适。在标准库中,当利用<span>string</span>对象构造<span>bitset</span>时,而<span>string</span>中的字符不是’<span>0</span>’或’<span>1</span>’的时候,抛出该异常</p></td></tr></tbody></table><p><strong>runtime_error</strong><strong>的子类:</strong></p><table><colgroup><col /><col /></colgroup><tbody><tr><td><p>异常名称</p></td><td><p>描述</p></td></tr><tr><td><p>range_error</p></td><td><p>计算结果超出了有意义的值域范围</p></td></tr><tr><td><p>overflow_error</p></td><td><p>算术计算上溢</p></td></tr><tr><td><p>underflow_error</p></td><td><p>算术计算下溢</p></td></tr><tr><td><p>invalid_argument</p></td><td><p>参数不合适。在标准库中,当利用<span>string</span>对象构造<span>bitset</span>时,而<span>string</span>中的字符不是’<span>0</span>’或’<span>1</span>’的时候,抛出该异常</p></td></tr></tbody></table><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"73f408aa-82cb-407d-b60b-20321037f5b1\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[#include<stdexcept>\nclass Person{\npublic:\n\tPerson(int age){\n\t\tif (age < 0 || age > 150){\n\t\t\tthrow out_of_range(\"年龄应该在0-150岁之间!\");\n\t\t}\n\t}\npublic:\n\tint mAge;\n};\n\nint main(){\n\n\ttry{\n\t\tPerson p(151);\n\t}\n\tcatch (out_of_range& ex){\n\t\tcout << ex.what() << endl;\n\t}\n\t\n\tsystem(\"pause\");\n\treturn EXIT_SUCCESS;\n}]]></ac:plain-text-body></ac:structured-macro><h3><span>7.3.2 </span>编写自己的异常类</h3><p>① 标准库中的异常是有限的;</p><p>② 在自己的异常类中,可以添加自己的信息。(标准库中的异常类值允许设置一个用来描述异常的字符串)。</p><ol><li><strong> </strong><strong>如何编写自己的异常类?</strong></li></ol><p>① 建议自己的异常类要继承标准异常类。因为<span>C++</span>中可以抛出任何类型的异常,所以我们的异常类可以不继承自标准异常,但是这样可能会导致程序混乱,尤其是当我们多人协同开发时。</p><p>② 当继承标准异常类时,应该重载父类的<span>what</span>函数和虚析构函数。</p><p>③ 因为栈展开的过程中,要复制异常类型,那么要根据你在类中添加的成员考虑是否提供自己的复制构造函数。</p><ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\" ac:macro-id=\"3d96c668-8f3e-4df8-97de-f4fcefc27cff\"><ac:parameter ac:name=\"language\">cpp</ac:parameter><ac:plain-text-body><![CDATA[//自定义异常类\nclass MyOutOfRange:public exception\n{\npublic:\n\tMyOutOfRange(const string errorInfo)\n\t{\n\t\tthis->m_Error = errorInfo;\n\t}\n\n\tMyOutOfRange(const char * errorInfo)\n\t{\n\t\tthis->m_Error = string( errorInfo);\n\t}\n\n\tvirtual ~MyOutOfRange()\n\t{\n\t\n\t}\n\tvirtual const char * what() const\n\t{\n\t\treturn this->m_Error.c_str() ;\n\t}\n\n\tstring m_Error;\n\n};\n\nclass Person\n{\npublic:\n\tPerson(int age)\n\t{\n\t\tif (age <= 0 || age > 150)\n\t\t{\n\t\t\t//抛出异常 越界\n\t\t\t//cout << \"越界\" << endl;\n\t\t\t//throw out_of_range(\"年龄必须在0~150之间\");\n\n\t\t\t//throw length_error(\"长度异常\");\n\t\t\tthrow MyOutOfRange((\"我的异常 年龄必须在0~150之间\"));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis->m_Age = age;\n\t\t}\n\t\t\n\t}\n\n\tint m_Age;\n};\n\n\nvoid test01()\n{\n\ttry\n\t{\n\t\tPerson p(151);\n\t}\n\tcatch ( out_of_range & e )\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (length_error & e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n\tcatch (MyOutOfRange e)\n\t{\n\t\tcout << e.what() << endl;\n\t}\n}]]></ac:plain-text-body></ac:structured-macro></ac:layout-cell><ac:layout-cell><p><br /></p></ac:layout-cell></ac:layout-section></ac:layout>","representation":"storage","_expandable":{"content":"/rest/api/content/58954474"}},"_expandable":{"editor":"","view":"","export_view":"","styled_view":"","anonymous_export_view":""}},"extensions":{"position":"none"},"_links":{"webui":"/pages/viewpage.action?pageId=58954474","edit":"/pages/resumedraft.action?draftId=58954474","tinyui":"/x/6pKDAw","collection":"/rest/api/content","base":"https://wiki.suncaper.net","context":"","self":"https://wiki.suncaper.net/rest/api/content/58954474"},"_expandable":{"container":"/rest/api/space/2CT","metadata":"","operations":"","children":"/rest/api/content/58954474/child","restrictions":"/rest/api/content/58954474/restriction/byOperation","history":"/rest/api/content/58954474/history","ancestors":"","descendants":"/rest/api/content/58954474/descendant","space":"/rest/api/space/2CT"}}