版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C+之异常处理一个好的程序应该能对多种不同的特殊情况,做出不同的反应,对于突发情 况也应有对应的处理方法。 我们在编程时应考虑到各种突发情况, 并在程序中给 出解决方案,使程序的健壮性增强。假设有一个司机从 A 地开车前往 B 地。若在某处有一岔路口, 一般选择左边, 路程会近一些。但当司机选择左边, 将车开到途中时发现正在修路 (突发情况) , 无法通过。这时,司机就会掉头回到刚才的岔路口处,重新选择右边的路,继续 前进。我们所编的程序也应该像这样, 有一定的智能化的设计。 这就要求在编写程 序时,应该试着确定程序可能出现的错运,然后加入处理错误的代码。例如:当 程序执行文件 I0 操作时,
2、应测试文件打开以及读写操作是否成功,并且在出 现错误时做出正确的反应。 随着程序复杂性的增加, 为处理错误而必须包括在程 序中代码的复杂性也相应地增加了。为使程序更易于测试和处理错误,C+实现了异常处理机制。一、 异常概念1 异常的概念程序的错误, 一种是编译错误, 即语法错误。 如果使用了错误的语法、 函数、 结构和类, 程序就无法被生成运行代码。 另一种是在运行时发生的错误, 它分为 不可预料的逻辑错误和可以预料的运行异常。运行异常,可以预料,但不能避免,它是由系统运行环境造成的。如,内存 空间不足,而程序运行中提出内存分配申请时,得不到满足,就会发生异常: #include/void f
3、(char *str)ifstream source(str); 打开 str 串中的文件if(source fail()打不开cerr Error opening the file: str endl;exit(1) ; 退出程序/ 当程序对文件打不开时,程序会打印提示信息,并由 exit (1)函数退出。 这样就不至于会因为文件打不开而导致整个程序在运行过程中停滞或错乱。2异常的基本思想在小型程序中, 一旦发生异常, 一般是将程序立即中断运行, 从而无条件释 放所有资源。 对于大型程序来说, 运行中一旦发生异常, 应该允许恢复和继续运行。恢复的过程就是把产生异常所造成的恶劣影响去掉,中间可
4、能要涉及一系列 的函数调用链的退栈,对象的析构,资源的释放等。继续运行就是异常处理之后, 在紧接着异常处理的代码区域中继续运行。在C+中,异常是指从发生问题的代码区域传递到处理问题的代码区域的一 个对象。见图 :发生异常的地方在函数k()中,处理异常的地方在其上层函数 f()中,处理异 常后,函数k()和g()都退栈,然后程序在函数f()中继续运行。如果不用异常 处理机制,在程序中单纯地嵌入错误处理语句,要实现这一目的是艰难的。异常的基本思想是:(1) 实际的资源分配(如内存申请或文件打开)通常在程序的低层进行,如图 中的k()。(2) 当操作失败、无法分配内存或无法打开一个文件时在逻辑上如何
5、进行 处理通常是在程序的高层,如图中的f(),中间还可能有与用户的对话。(3) 异常为从分配资源的代码转向处理错误状态的代码提供了一种表达方式。如果还存在中间层次的函数,如图中的g(),则为它们释放所分配的内存提供了机会,但这并不包括用于传递错误状态信息的代码。从中可以看出,C什异常处理的目的,是在异常发生时,尽可能地减小破坏,周密地善后,而不去 影响其它部分程序的运行。-这在大型程序中是非常必要的。例如对于以前所讲的程序调用关系,如处理文件打开失败异常的方法,那么, 异常只能在发生的函数k()中进行处理,无法直接传递到函数 f()中,而且调用 链中的函数g()的善后处理也十分困难。二、异常的
6、实现使用异常的步骤是:(1) 定义异常(try语句块) 将那些可能产生错误的语句框定在try语句中;(2) 定义异常处理(catch语句块)将异常处理的语句放在 catch块中,以便异常被传递过来时就处理它;(3) 抛掷异常(throw语句)检测是否产生异常,若产生异常,则抛掷异常。例如,下面的程序,设置了防备文件打不开的异常:例题 1#include #include #include void main(int argc,char *argv) ifstream source(argv1); / 打开文件char line128;try if(source.fail() / 如果打开失败t
7、hrow argv1;catch (char *s)couterror opening the filesendl;exit(1);while(!source.eof() source.getline(line,sizeof(line);coutline 提示符后输入命令 ch10_1 abc.txt 屏幕显示结果为: error opening the file abc.txt 若输入命令 ch10_1 xyz.txt 则屏幕显示结果为: How are you ? Fine!例题 2:一个除零异常: #include double Div(double,double);void main(
8、)trycout7.3/2.0=Div(7.3,2.0)endl;cout7.3/0.0=Div(7.3,0.0)endl;cout7.3/1.0=Div(7.3,1.0)endl; catch(double) coutexcept of deviding zero!n;coutThat is ok.n;double Div(double a,double b) if(b=0.0) throw b; return a/b; 运行结果为:7.3/2.0=3.65except of deviding zero!That is ok.三、异常处理机制在处理程序和语句之间的相互作用使异常在大型应用程序
9、中变得复杂。 通常人们 希望抛掷被及时捕获,以避免程序突然终止。此外,跟踪抛掷很重要,因为捕获 确定该程序的后继进展。 例如,抛掷和捕获可以用来重新开始程序内的一个过程, 或者从应用程序的一部分跳到另一部分,或者回到菜单。 例如,项目的代码说明了异常处理机制。函数f()中的catch(.) 块,参数为省略号,定义一个默认的异常处理程序通常这个处理程序应在所有异常处理块的最后, 因为它与任何 throw 都匹配,目 的是为避免定义的异常处理程序没能捕获抛掷的异常而使程序运行终止。函数 h() 中的 catch(Size) 块,包含有一个抛掷异常语句 throw 10,当实际执行 这条语句时,将沿
10、着调用链向上传递被函数 f() 中的 catch(.) 所捕获。如果没 有 f() 中的 catch(.) ,那么,异常将被系统的 terminate() 函数调用,后者按 常规再调用 abort() 。函数 h1() 中的抛掷 throw Size ,由于不在本函数的 try 块中,所以只能沿函数调用链向上传递, 结果被 h() 中的 catch(Size) 捕获。函数 h1() 中的抛掷 throw Range,在try块中,所以首先匹配try块后的异常处理程序,可是没有被捕获, 因而它又沿函数调用链向上, 在函数 f() 中, catch(Range) 块终于捕获了该抛掷。 函数 h1(
11、) 中的 catch(Size) 块,包含一个抛掷 throw ,没有带参数类型,它表示 将捕获到的异常对象重新抛掷出去,于是,它将沿函数调用链向上传递,在 h() 中的 catch(Size) 块,捕获了该抛掷。函数 h2() 中的抛掷 throw Matherr ,首先传递给 h1() 中的 catch 块组,但未能 被捕获,然后继续沿调用链向上, 在 h() 中的 catch(Matherr) 块,捕获了该抛掷。 函数 h3() 中的抛掷 throw Size ,向上传递给 h1() 中的 catch 块组,被 catch(Size) 块捕获。四、使用异常的方法可以把多个异常组成族系。
12、构成异常族系的一些实力又数学错误异常族系和文件 处理错误异常族系。在C+弋码中把异常组在一起有两种方式:异常枚举族系和 异常派生层次结构。例如,下面的弋码是一个异常枚举族系的例子:enmu FileErrorsnonExist,wrongformat,diakSeekError,.;int f()try /. throw wrongFormat;catch(FileErrors fe) switch(fe) case nonExist:/.case wrongFormat:/.case diskSeekError:/./.在 try 块中有一个 throw ,它抛掷一个 FileError 枚
13、举中的常量。这个抛掷可被 catch(FileErrors) 块捕获到,接着后者执行一个 switch ,对照情况列表,匹配 捕获到的枚举常量值。 上面的异常族系也可按异常派生层次结构来实现, 如下例所示:class FileErrors;class NonExist:public FileErrors;class WrongFormat:public FileErrors;class DiskSeekError:public FileErrors;int f() try /. throw WrongFormat; catch(NonExist) /. catch(DiskSeekError)
14、 /. catch(FileErrors) /./.上面的各异常处理程序块定义了针对类 NonExist 和 DiskSeekError 的派生异常 类对象,针对 FileErrors 的异常处理,既捕获 FileErrors 类对象,也捕获 WrongFormat对象。异常捕获的规则除了前面所说的,必须严格匹配数据类型外, 对于类的派生,下列情况可以捕获异常:(1) 异常处理的数据类型是公有基类,抛掷异常的数据类型是派生类;(2) 异常处理的数据类型是指向公有基类的指针, 抛掷异常的数据类型是指向派 生类的指针。对于派生层次结构的异常处理, catch 块组中的顺序是重要的。因为catch(
15、基类)总能够捕获throw派生类对象。所以catch(基类)块总是 放在catch(派生类)块的后面,以避免catch(派生类)永远不能捕获异常。五、 异常的再提出 (rethrowing)有时候会发生一个异常处理句柄接收了一个异常却发现不能处理这个异常的情况。这时, 这个异常可以被再提出以便于其它句柄能够更好的处理。异常的再提出可以通过一个空的 throw 表达语句来实现。但是,这种表达语句只能出现于一个异常处理句柄中。例如,void f()tryshowWindow(); / 提出 CoDialogException 类异常catch (CoWindowException& WinExc)
16、Win Exc.repai nt();throw; II异常再提出void g()tryf();catch (CoDialogExcepti on& DialogExc) /*异常处理语句*/ catch (CoWi ndowExcepti on& Win dowExc) /*异常处理语句*/ 上述例子中,尽管CoDialogException类异常是由函数 f()中的CoWindowException类处理句 柄再提出的,但是它仍然由函数g()中的CoDialogException类异常处理句柄来处理。此外,任何异常都可以通过一种特殊的接收方式catch(.)来接收和处理。例如下面例子中的f
17、()函数可以接收任何异常并再提出。void f()tryshowWi ndow();catch(.) /接收任何异常/某些处理语句throw;值得注意的是,异常的再提出并不对异常对象进行进一步的拷贝。#in clude #in clude using n amespace std;en um SUCCESS, FAILURE;class Filepublic: File (const char *) public: bool lsValid() const retur n false; public: int Ope nN ew() const return FAILURE; |;class
18、Excepti on /*.*/; ge neral base class for exceptio nsclass FileExcepti on: public Excepti onpublic: FileExcepti on(const char *p) : s(p) public: const char * Error() const retur n s.c_str(); private: string s;void fun c(File & );int mai n()try /outer tryFile f (db.dat);func (f) ; / 1catch(.) / 7/thi
19、s han dler will catch the re-throw n excepti on;/no te: the same excepti on type is requiredcoutre-throw n exceptio n caught;return 0;void fun c(File & f)try /inner tryif (f.IsValid() = false )throw FileExceptio n(db.dat); / 2catch(FileException &fe) / 3/first cha nee to cope with the excepti oncout
20、invalid file specification fe.Error()endl;if (f.Ope nN ew() != SUCCESS) (5)/re-throw the original exception and let a higher handler deal with it throw; / 6 在上面的例子中,函数 func()在 main()中的try block里被调用(1)。第二个在 func()中的 try block抛出一个 FileException类型的异常(2)。这个异常被func()内的catch block所捕获(3 )。那个 catch block试图通
21、过打开一个新文件进行补救,但是失败了(5),并且FileException异常被重新抛出(6)。最终,那个重新抛出的异常被main()中的catch()所捕获(7)。六在异常对象中携带更多的信息异常对象如同其它类对象一样可以携带信息。所以一个异常对象可以被用来将一些有用信息从提出点携带到接收处理点。这些信息可以是当程序在运行过程中出现非正常情况时程序使 用者想要知道的。例如一个程序使用者可能想知道一个矢量的下标,当这个矢量下标超限时。class CoVectorpublic:CoVector(i nt);class Ran ge;int& operator (int i);protected:
22、int* pI nt_;int theSize;class CoVector:Ra ngepublic:CoVector:Ra nge(i nt);int in dex_;CoVector:Ra nge:Ra nge(i nt i):i ndex_(i) /* . */ int& CoVector:operator(i nt i)if (0 = i & i theSize_)return pl nt_i;throw Ran ge(i);void f(const CoVector& v)tryint temp = v169;catch (const CoVector:Range& r)cout
23、bad index = r.index_ endl;实际上,catch后面括弧中的表达语句实际上类似于函数的参数定义。七、句柄的次序异常处理句柄有先后次序之分。因为一个派生 (derived) 异常类对象可以被几个句柄接收,所 以在排列异常处理句柄顺序时应该特别小心。 另外, 一个类型严格匹配的处理句柄并不比一 个需要类型转换的处理句柄更有优先权。例如下面的例子就很糟糕。class CoWindow /* . */ ;class CoButton:public CoWindow /* . */ ;void f(int v)typedef void (*PCF)(const char*);try
24、if (v) throw &v;/其它表达语句catch (void * pVoid) /* . */ catch (PCF pFunction) /* . */ catch (const CoWindow& win) /* . */ catch (const CoButton& button) /* . */ catch (.) /* . */ return;在上面例子中,(void *)处理句柄不可能允许它后面的PCF处理句柄被调用。类似的,因为CoWi ndow类处理句柄将会接收任何CoWi ndow类及它的衍生类对象,所以CoButt on类的处理句柄也不会被调用。依赖于你所使用的编译
25、器,有的编译器可能在编译时警告你一个从类B派生来的类D句柄放在类B句柄后面。但是,如果一个接收任何异常的句柄catch(.J不是最后一个句柄,编译器会给出编译错误。派生类组织异常;Class Matherr;Class Overflow: public Matherr;Class Un derflow: public Matherr;Class:Zerodivide:public Matherr;Try Cath (Overflow)Cath (Uu nderflow)Cath (Zerodivideflow)Cath (Matherr)八、异常提出过程中的对象构造和析构当一个程序由于异常而中
26、断时,所有的从try开始构造的自动变量类的对象都会被清除、释放。这种调用自动变量类的析构函数的过程称为堆栈清除(stack unwinding)。下面给出了一个实例。class CoClasspublic:int v_;CoClass(int v = 0) : v_(v)cout CoClass(int): v_ endl;CoClass()cout CoClass(): v_ endl;class CoErrorpublic:int v_;CoError(int v = 0):v_(v)cout CoError(int): v_ endl;CoError(const CoError& ve)
27、:v_(ve.v_)cout CoError(const CoError&): v_ endl;CoError()cout CoError(): v_ endl;int f(int v)if (v = 13)CoClass vc(0);throw CoError(v); return v;int main()tryCoClass vc(169);f(13);catch (const CoError& e)cout Caught : e.v_ endl;return 0;这个例子给出了下面输出结果。当 catch( const CoError e) 时CoClass(int): 169CoCla
28、ss(int): 0CoError(int): 13CoError(const CoError&): 13CoClass(): 0CoClass(): 169Caught : 13CoError(): 13CoError(): 13当 catch( const CoError & e) 时CoClass(int): 169CoClass(int): 0CoError(int): 13CoClass(): 0CoClass(): 169Caught : 13CoError(): 13当一个异常在一个类对象的构造过程中被提出时,如果这个类对象中包含了其它成员类对 象,那么那些在异常提出前完成了构造
29、的成员类对象的析构函数会被调用来清除已构造了的 成员对象。 而那些没有完成构造的成员类对象的析构函数不会被调用。例如, 如果在构造一个类对象的数组过程中一个异常被提出, 那么只有那些完成了构造的对象的析构函数才被调 用来进行堆栈清除。接收一个析构函数提出的异常也是可能的。 将调用析构函数的函数放在 try 后面的程序块中, 并且提供适当的类型句柄就可以了。九、函数的异常规范 一个函数的参数串称之为这个函数的签名 (signature) ,因为它经常被用来区别一个函数实体。 一个函数由四个部分组成。 ( 1)返回类型。(2)函数名。(3)函数签名。(4)函数体。前三 个部分常称作为函数原型 (f
30、unction prototype). 函数原型提供给了编译器关于这个函数的类型 信息。编译器通过这些信息进行类型诊断。 通常一个函数的使用者只需要知道这个函数原型 就可以了。 但是如果使用者想写这个函数所提出的所有异常句柄, 那么这个使用者就需要知 道这些异常类型。要 解 决这 个问 题 , 一个 函数 原 型或 者定 义 (definition) 可以 包括 异 常 规 范 (exception specifications) 。异常规范出现在一个函数定义后面。 它列出了这个函数可以直接或间接提出 的异常。例如下面给出了一些函数的原型void f() throw(v1, v2, v3);v
31、oid g();void h() throw();void e() throw(WClass *);f()的定义表明了这个函数可以提出类型v1,v2和v3,以及它们的派生类型。如果函数f()提出除此以外的其它异常,程序在运行中会产生一个运行错误。函数g()的定义没有包含异常规范。这意味着它可以提出任何异常。而函数h()则不能提出任何异常。函数e()可以提出WClass 类的指针型异常, 或者由 WClass 派生来的类指针异常。一个函数后面的异常规范并不属于这个函数指针中的一部分。然而当一个指向函数的指针赋值给指向另一个函数的指针时, 只有在被赋值的指针函数包含了赋值指针函数的异常时才允 许。
32、例如void (*pf1)(); / 没有异常规范void (*pf2)() throw(int); / 只能提出整型异常void f()pf1 = pf2; / 可以, pf1 要求比较宽松pf2 = pf1; / 错误, pf2 要求更严格下面函数void f() throw(v1,v2,v3)SomeAction();相当于void f()trySomeAction();catch(v1 v)throw; / 在提出catch(v2 v)throw; / 在提出catch(v3 v)throw; / 在提出catch(.)unexpected(); / 定义在头文件 中当一个函数提出的异
33、常没有包括在这个函数的异常规范中时,程序会自动调用函数unexpected() 来 处 理 这 个 异 常 。 函 数 unexpected() 的 缺 省 行 为 是 调 用 使 用 者 通 过 set_u nexpected ()函数注册的函数。假如没有任何函数通过set_u nexcepted()来注册,函数unexpected()则调用函数terminate()。这三个函数的原型如下。void unexpected();typedef void (*unexpected_handler)();unexpected_handler set_unexpected(unexpected_ha
34、ndler) throw();void terminate();函数set_unexpected()的返回值是前一次通过set_unexpected()注册的函数指针。函数terminate()可以通过unexpected()来调用,或者当找不到一个异常句柄时程序自动调用。 termi nate()函数的缺省行为是调用函数abort()。这个缺省函数即刻中止程序运行。你可以改变当一个异常没有出现于函数的异常规范中时的程序中止方式。如果你不想你的程序通过调用abort()来中止,你可以定义另外一个函数来替代它。这样的函数通过set_terminate()函数来注册并由termi nate()调用
35、。函数 set_termi nate()的定义如下 typedef void (*terminate_handler)();terminate_handler set_terminate(terminate_handler) throw();这个函数的返回值是前一次通过函数set_terminate() 来注册的函数指针。头文件 定义了处理异常的函数, 类及其数据成员和成员函数。十、 具有异常规范的虚拟函数一个虚拟函数也可以定义异常规范。 但是,一个子类中的虚拟函数提出的异常不能比父 类中的虚拟函数提出的异常多。 也就是说, 一个子类中的虚拟函数规范不能比父类中的虚拟 函数规范更宽松。 而一个
36、子类中的虚拟函数提出的异常比父类中的虚拟函数提出的异常少则 是允许的。例如class CoWindowpublic:virtual void show() throw(int);virtual void getFocus() throw(int, char);class CoDialogBox:public CoWindowpublic:virtual void show() throw(int, char); / 错误,异常规范较宽松virtual void getFocus() throw(int); / 可以,异常规范更严历;编译器针对 CoDialog:show() 会给出编译错误,
37、因为在运行时这个函数既可以提出 int 类型异 常也可以提出 char 类型异常。而在父类定义中,这个虚拟函数只能提出 int 类型异常。另一 方面, CoDialog:getFocus() 是允许的,因为它的规范比父类中的虚拟函数的异常规范要求更 严历。例题:面我们用示例演示一下异常处理1 #include stdafx.h2 #include 3 template 5 T Div(T x,T y) if(y=0) throw y;/ 抛出异常return x/y; int main() int x=5,y=0;double x1=5.5,y1=0.0;/ 被检查的语句try std:cou
38、tx/y=Div(x,y)std:endl;std:coutx1/y1=Div(x1,y1)std:endl;catch(int)/异常类型std:cout 除数为 0, 计算错误!std:endl;/ 异常处理语句 catch(double)/异常类型std:cout 除数为 0.0, 计算错误!std:endl;/ 异常处理语句 29return 0;结果:看了上述的示例代码,也许有人会问,第二个双精度类型的除法计算也应该抛出异 常才对啊,在实际的运行过程中并非如此,其实该双精度类型除法函数根本没有被 执行过。以上程序的执行规程为:调用函数Div(x,y)时发生异常,由函数 Div中的语句
39、throw y抛出异常,并不在往下执行return x/y ,接着catch捕获int类型的异常并处理异常,最后直接执行return 0。因此函数 Div(x1,y1)和catch(double)模块根本没有被执行。如果,我们把y的值改为1,则结果就变成为:如果在执行try语句模块时,没有发生异常,则catch语句块不起作用,流程转到其后的语句继续执行。从上述两个结果中可知第一次throw抛出的int类型所以找到处理该类型的 catch,而第二次是抛出 double类型所找到的是处理double类型的 catch。下面对异常处理补充几点:try 和catch块中必须要用花括号括起来,即使 花括
40、号内只有一个语句也不能省略花括号;(2)try 和catch必须成对出现,一个try_catch结果中只能有一个try块,但可以有多个 catch块,以便与不同的异常信息匹配;(3)如果在catch块中没有指定异常信息的类型,而用删节号,则表示它可以捕获任何类型的异常信息;(4)如果throw不包括任何表达式,表示它把当前正在处理的异常信息再次抛出,传给其上一层的catch来处理;(5)C+中一旦抛出一个异常,如果程序没有任何的捕获,那么系统将会自动调用一个系统函数 terminate ,由它调用 abort 终止程序 ;( 开发工具:最后还是一样,我将用一个示例来总结一下今天所讲的内容#in
41、clude template T Div(T x,T y) if(y=0)throw y;/抛出异常return x/y;int main() int x=5,y=1;double x1=5.5,y1=0.0;try std:coutx/y=Div(x,y)std:endl;std:coutx1/y1=Div(x1,y1)std:endl;try std:cout任意类型异常!std:e ndl;throw;/抛出当前处理异常信息给上一层catch(i nt)/异常类型0 std:cout除数为0,计算错误!std:endl;异常处理语catch(double)/异常类型 std:cout除数
42、为0.0,计算错误!std:endl;异常处理语return 0;异常的误用异常处理并不是用来限制出现错误,一些程序员可能会简单的使用它来作为循环的选择控制结构。例如,一个简单的应用程序让用户输入数据直到一个特定的条件满足:#in elude usingnamespace std;classExit; /usedas excepti onobjectintmai n()int num;cout en ter a nu mber; 99 to exite ndl;trywhile (true)/i nfin itely|cinnum;if (num = 99)throwExit();/exitt
43、he loopcoutyouen tered: num enter anothernumber endl;catch (Exit& )cout game over e ndl;return 0;在上面的例子中,程序员把一个无限循环放在了try block中,throw语句终止循环并且把控制权传递给后面的catch语句。这种编程样式不应该被推荐。它的效率会非常低下因为异常处理存在。在上面小的演示程序中,或许仅仅是程序样式的差 异,但是在大规模的应用系统中,使用异常处理来作为控制选择控制结构的话,那 么将会带来显著的效率损失。标准异常C+碇义了一个标准异常层次,当在运行时发生反常情形时抛出。标准异
44、常类从 std:exception (在头文件中定义)派生。这一层次使得应用程序能够catch(std:excepti on&exc)/han dleexcepti on of typestd:excepti onas well as/anyexcepti onderived from it那些通过语言内建操作符抛出的标准异常是:std:bad_alloc /by operator newstd:bad_cast /by operator dyn amic_cast std:bad_typeid /by operator typeidstd:bad_excepti on/throw nwhe n an excepti onspecificatio nof所有的标准异常都提供
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《女性领导力》课件
- 《护理服务提升》课件
- 2024合同模板舞台音响租赁合同范本
- 《多式联运单证》课件
- 《大气新导则例题》课件
- 2024个人代理合同模板
- 《担保法实务培训》课件
- 2024个人商业用房借款合同范本
- 2024如何进行合同评审?信托与贷款
- 军人抚恤优待条例培训2024
- 言语的第三思维结合语境
- TD-T 1070.4-2022 矿山生态修复技术规范 第4部分:建材矿山
- 城市轨道交通设备系统之通风空调系统概述
- 绿盟极光漏洞扫描工具使用方法
- APQP-4-08产品质量策划总结和认定报告
- 五年级语文上学情分析(每一课都有,全)
- 四升五数学测试题(2022年整理)
- mom-knows-best-诗歌教学讲解课件
- 2022年征信知识竞赛基础题题库(含各题型)
- 新概念英语青少版入门级Starter A 结业测试 含答案
- 临床科室医疗质量管理与持续改进培训课件
评论
0/150
提交评论