版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第10章 运算符重载10.1 什么是运算符重载10.2 运算符重载的方法10.3 重载运算符的规则10.4 运算符重载函数作为类成员函数和友元函数10.5 重载双目运算符10.6 重载单目运算符10.7 重载流插入运算符和流提取运算符10.8 不同类型数据间的转换第10章 运算符重载所谓重载,就是重新赋予新的含义函数重载就是对一个已有的函数赋予新的含义,使之实现新功能运算符也可以重载实际上,我们已经在不知不觉之中使用了运算符重载现在要讨论的问题是: 用户能否根据自己的需要对C+已提供的运算符进行重载,赋予它们新的含义,使之一名多用譬如,能否用“+”号进行两个复数的相加在C+中不能在程序中直接用
2、运算符“+”对复数进行相加运算用户必须自己设法实现复数相加例如用户可以通过定义一个专门的函数来实现复数相加见例10.110.1 什么是运算符重载第10章 运算符重载例10.1 通过函数来实现复数相加#include using namespace std;class Complex /定义Complex类public:Complex( )real=0;imag=0; /定义构造函数Complex(double r,double i)real=r;imag=i; /构造函数重载Complex complex_add(Complex &c2); /声明复数相加函数void display( );
3、/声明输出函数 private:double real; /实部double imag; /虚部;Complex Complexcomplex_add(Complex &c2)Complex c;c.real=real+c2.real;第10章 运算符重载c.imag=imag+c2.imag;return c;void Complexdisplay( ) /定义输出函数cout(real,imagi)endl;int main( )Complex c1(3,4),c2(5,-10),c3;/定义3个复数对象c3=plex_add(c2); /调用复数相加函数coutc1=; c1
4、.display( );/输出c1的值coutc2=; c2.display( );/输出c2的值coutc1+c2=; c3.display( );/输出c3的值return 0;运行结果如下: c1=(3+4i)c2=(5-10i)c1+c2=(8,-6i)第10章 运算符重载结果无疑是正确的,但调用方式不直观太烦琐,使人感到很不方便能否也和整数的加法运算一样,直接用加号“+”来实现复数运算呢?如c3=c1+c2;编译系统就会自动完成c1和c2两个复数相加的运算如果能做到,就为对象的运算提供了很大的方便这就需要对运算符“+”进行重载第10章 运算符重载运算符重载的方法是定义一个重载运算符的
5、函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算也就是说,运算符重载是通过定义函数实现的运算符重载实质上是函数的重载重载运算符的函数一般格式如下: 函数类型 operator 运算符名称 (形参表列) 对运算符的重载处理 例如,想将“+”用于Complex类(复数)的加法运算,函数的原型可以是这样的: Complex operator+ (Complex& c1,Complex& c2);10.2 运算符重载的方法第10章 运算符重载在定义了重载运算符的函数后,可以说: 函数operator+重载了运算符+为了说明在运算符重载后,执行表达式就是调用函数的过程,可以把两个
6、整数相加也想像为调用下面的函数: int operator + (int a,int b)return (a+b);如果有表达式5+8,就调用此函数,将5和8作为调用函数时的实参,函数的返回值为13这就是用函数的方法理解运算符可以在例10.1程序的基础上重载运算符“+”,使之用于复数相加第10章 运算符重载例10.2 改写例10.1,重载运算符“+”,使之能用于两个复数相加#include using namespace std;class Complexpublic:Complex( )real=0;imag=0;Complex(double r,double i)real=r;imag=i
7、;Complex operator+(Complex &c2);/声明重载运算符的函数void display( ); private:double real;double imag;Complex Complexoperator+(Complex &c2) /定义重载运算符的函数 Complex c;c.real=real+c2.real;c.imag=imag+c2.imag;第10章 运算符重载return c;void Complexdisplay( ) cout(real,imagi)endl;int main( ) Complex c1(3,4),c2(5,-10),c3;c3=c
8、1+c2; /运算符+用于复数运算coutc1=;c1.display( );coutc2=;c2.display( );coutreal+c2.real,this-real就是c1.real在10.2节中已说明,在将运算符函数重载为成员函数后,如果出现含该运算符的表达式,如c1+c2,编译系统把它解释为c1.operator+(c2) 即通过对象c1调用运算符重载函数,并以表达式中第二个参数(运算符右侧的类对象c2)作为函数实参运算符重载函数的返回值是Complex类型,返回值是复数c1和c2之和(Complex(c1.real + c2.real,c1.imag+c2.imag)第10章
9、运算符重载运算符重载函数除了可以作为类的成员函数外,还可以是非成员函数可以将例10.2改写为例10.3例10.3 将运算符“+”重载为适用于复数加法,重载函数不作为成员函数,而放在类外,作为Complex类的友元函数#include using namespace std;class Complexpublic:Complex( )real=0;imag=0;Complex(double r,double i)real=r;imag=i;friend Complex operator + (Complex &c1,Complex &c2);/重载函数作为友元函数void display( );
10、 private:double real;double imag;第10章 运算符重载;Complex operator + (Complex &c1,Complex &c2) /定义作为友元函数的重载函数return Complex(c1.real+c2.real, c1.imag+c2.imag);void Complexdisplay( )cout(real,imagi)endl;int main( )Complex c1(3,4),c2(5,-10),c3;c3=c1+c2;coutc1=; c1.display( );coutc2=; c2.display( );coutc1+c2
11、=; c3.display( );第10章 运算符重载与例10.2相比较,只作了一处改动,将运算符函数不作为成员函数,而把它放在类外,在Complex类中声明它为友元函数同时将运算符函数改为有两个参数在将运算符“+”重载为非成员函数后,C+编译系统将程序中的表达式c1+c2解释为operator+(c1,c2)即执行c1+c2相当于调用以下函数: Complex operator + (Complex &c1,Complex &c2)return Complex(c1.real+c2.real, c1.imag+c2.imag);求出两个复数之和运行结果同例10.2为什么把运算符函数作为友元函
12、数呢?因为运算符函数要访问Complex类对象中的成员如果运算符函数不是Complex类的友元函数,而是一个普通的函数,它是没有权利访问Complex类的私有成员的第10章 运算符重载在10.2节中曾提到过: 运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数现在分别讨论这3种情况首先,只有在极少的情况下才使用既不是类的成员函数也不是友元函数的普通函数,原因是上面提到的,普通函数不能直接访问类的私有成员在剩下的两种方式中,什么时候应该用成员函数方式,什么时候应该用友元函数方式?二者有何区别呢?如果将运算符重载函数作为成员函数,它可以通过th
13、is指针自由地访问本类的数据成员,因此可以少写一个函数的参数但必须要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,第10章 运算符重载而且与运算符函数的类型相同因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义在例10.2中,表达式c1+c2中第一个参数c1是Complex类对象,运算符函数返回值的类型也是Complex,这是正确的如果c1不是Complex类,它就无法通过隐式this指针访问Complex类的成员了如果函数返回值不是Complex类复数,显然这种运算是没有实际意义的如想将一个复数和一个整数相加,如c1+i,可以
14、将运算符重载函数作为成员函数,如下面的形式: Complex Complexoperator+(int &i) /运算符重载函数作为Complex类的成员函数return Complex(real+i,imag);第10章 运算符重载注意在表达式中重载的运算符“+”左侧应为Complex类的对象,如c3=c2+i;不能写成c3=i+c2; /运算符“+”的左侧不是类对象,编译出错如果出于某种考虑,要求在使用重载运算符时运算符左侧的操作数是整型量(如表达式i+c2,运算符左侧的操作数i是整数),这时是无法利用前面定义的重载运算符的,因为无法调用i.operator+函数可想而知,如果运算符左侧的
15、操作数属于C+标准类型(如int)或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数如果函数需要访问类的私有成员,则必须声明为友元函数可以在Complex类中声明: 第10章 运算符重载friend Complex operator+(int &i,Complex &c); /第一个参数可以不是类对象在类外定义友元函数: Complex operator+(int &i, Complex &c) /运算符重载函数不是成员函数return Complex(i+c.real,c.imag);将双目运算符重载为友元函数时,在函数的形参表列中必须有两个参数,不能省略,形参的顺序
16、任意,不要求第一个参数必须为类对象但在使用运算符的表达式中,要求运算符左侧的操作数与函数第一个参数对应,运算符右侧的操作数与函数的第二个参数对应如c3=i+c2; /正确,类型匹配c3=c2+i; /错误,类型不匹配第10章 运算符重载请注意,数学上的交换律在此不适用如果希望适用交换律,则应再重载一次运算符“+”如Complex operator+(Complex &c, int &i) /此时第一个参数为类对象return Complex(i+c.real,c.imag);这样,使用表达式i+c2和c2+i都合法,编译系统会根据表达式的形式选择调用与之匹配的运算符重载函数可以将以上两个运算符
17、重载函数都作为友元函数,也可以将一个运算符重载函数(运算符左侧为对象名的) 作为成员函数,另一个(运算符左侧不是对象名的)作为友元函数但不可能将两个都作为成员函数,原因是显然的C+规定,有的运算符(如赋值运算符下标运算符函数调用运算符)必须定义为类的成员函数,有的运算符则不能定义为类的成员函数(如流插入“”类型转换运算符)第10章 运算符重载由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为成员函数但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数在学习了本章第10.7节例10.9的讨论后,读者对此会有更深入的认识说明: 有的C+编译系统(如Vi
18、sual C+ 6.0)没有完全实现C+标准,它所提供不带后缀.h的头文件不支持把成员函数重载为友元函数上面例10.3程序在GCC中能正常运行,而在Visual C+ 6.0中会编译出错但是Visual C+所提供的老形式的带后缀.h的头文件可以支持此项功能,因此可以将程序头两行修改如下,即可顺利运行: #include 以后如遇到类似情况,亦可照此办理第10章 运算符重载双目运算符(或称二元运算符)是C+中最常用的运算符双目运算符有两个操作数,通常在运算符的左右两侧,如3+5,a=b,i10等在重载双目运算符时,不言而喻在函数中应该有两个参数下面再举一个例子说明重载双目运算符的应用10.5
19、重载双目运算符第10章 运算符重载例10.4 定义一个字符串类String,用来存放不定长的字符串,重载运算符“=”,“”,用于两个字符串的等于小于和大于的比较运算为了使读者便于理解程序,同时也使读者了解建立程序的步骤,下面分几步来介绍编程过程(1) 先建立一个String类: #include using namespace std;class Stringpublic:String( )p=NULL; /默认构造函数String(char *str); /构造函数void display( );private:char *p;/字符型指针,用于指向字符串;第10章 运算符重载StringS
20、tring(char *str) /定义构造函数p=str; /使p指向实参字符串void Stringdisplay( ) /输出p所指向的字符串coutp;int main( )String string1(Hello),string2(Book);string1.display( );cout”程序如下: #include #include using namespace std;class Stringpublic:String( )p=NULL;String(char *str);friend bool operator(String &string1,String &string2
21、);/声明运算符函数为友元函数void display( );private:char *p;/字符型指针,用于指向字符串;StringString(char *str)p=str;第10章 运算符重载void Stringdisplay( ) /输出p所指向的字符串cout(String &string1,String &string2)/定义运算符重载函数if(strcmp(string1.p,string2.p)0)return true;else return false;int main( )String string1(Hello),string2(Book);coutstring
22、2) (String &string1, String &string2);friend bool operator(String &string1,String &string2) /对运算符“”重载if(strcmp(string1.p,string2.p)0)return true;elsereturn false;bool operator(String &string1,String &string2) /对运算符“”重载if(strcmp(string1.p,string2.p)0)return true;else第10章 运算符重载return false;bool operat
23、or=(String &string1,String &string2) /对运算符“=”重载if(strcmp(string1.p,string2.p)=0)return true;elsereturn false;再修改主函数: int main( )String string1(Hello),string2(Book),string3(Computer);coutstring2)endl;/比较结果应该为truecout(string1string3)endl;/比较结果应该为false cout(string1=string2)endl; /比较结果应该为false return 0;
24、第10章 运算符重载运行结果为100结果显然是对的到此为止,主要任务基本完成(4) 再进一步修饰完善,使输出结果更直观下面给出最后的程序#include using namespace std;class Stringpublic:String( )p=NULL;String(char *str);friend bool operator(String &string1,String &string2);friend bool operator(String &string1,String &string2);friend bool operator=(String &string1,Stri
25、ng &string2);第10章 运算符重载void display( );private:char *p; StringString(char *str)p=str;void Stringdisplay( ) /输出p所指向的字符串cout(String &string1,String &string2)if(strcmp(string1.p,string2.p)0)return true;elsereturn false;bool operator(String &string1,String &string2)第10章 运算符重载if(strcmp(string1.p,string2.
26、p)(string1,string2)=1)string1.display( );cout;string2.display( );elseif(operator(string1,string2)=1)第10章 运算符重载string1.display( );cout;string2.display( );elseif(operator=(string1,string2)=1)string1.display( );cout=;string2.display( );coutBookBookComputerHello=Hello第10章 运算符重载增加了一个compare函数,用来对两个字符串进行比
27、较,并输出相应的信息这样可以减轻主函数的负担,使主函数简明易读通过这个例子,不仅可以学习到有关双目运算符重载的知识,而且还可以学习怎样去编写C+程序这种方法的指导思想是: 先搭框架,逐步扩充,由简到繁,最后完善边编程,边调试,边扩充千万不要企图在一开始时就解决所有的细节类是可扩充的,可以一步一步地扩充它的功能最好直接在计算机上写程序,每一步都要上机调试,调试通过了前面一步再做下一步,步步为营这样编程和调试的效率是比较高的读者可以试验一下第10章 运算符重载单目运算符只有一个操作数,如!a,-b,&c,*p,还有最常用的+i和-i等重载单目运算符的方法与重载双目运算符的方法是类似的但由于单目运算
28、符只有一个操作数,因此运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数下面以自增运算符“+”为例,介绍单目运算符的重载10.6 重载单目运算符第10章 运算符重载例10.5 有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算要求输出分和秒的值#include using namespace std;class Timepublic:Time( )minute=0;sec=0; /默认构造函数Time(int m,int s):minute(m),sec(s) /构造函数重载Time operat
29、or+( ); /声明运算符重载函数void display( )coutminute:sec=60)第10章 运算符重载sec-=60; /满60秒进1分钟+minute;return *this; /返回当前对象值int main( )Time time1(34,0);for (int i=0;i61;i+)+time1;time1.display( );return 0;运行情况如下: 34:134:234:5935:035:1 (共输出61行)第10章 运算符重载可以看到: 在程序中对运算符“+”进行了重载,使它能用于Time类对象“+”和“-”运算符有两种使用方式,前置自增运算符和后
30、置自增运算符,它们的作用是不一样的,在重载时怎样区别这二者呢?针对“+”和“-”这一特点,C+约定: 在自增(自减)运算符重载函数中,增加一个int型形参,就是后置自增(自减)运算符函数例10.6 在例10.5程序的基础上增加对后置自增运算符的重载修改后的程序如下:#include using namespace std;class Timepublic:Time( )minute=0;sec=0;Time(int m,int s):minute(m),sec(s)第10章 运算符重载Time operator+( );/声明前置自增运算符“+”重载函数Time operator+(int);
31、/声明后置自增运算符“+”重载函数void display( )coutminute:sec=60)sec-=60;+minute;return *this;/返回自加后的当前对象Time Timeoperator+(int)/定义后置自增运算符“+”重载函数Time temp(*this);sec+;第10章 运算符重载if(sec=60)sec-=60;+minute;return temp; /返回的是自加前的对象int main( )Time time1(34,59),time2;cout time1 : ;time1.display( );+time1;cout+time1: ;ti
32、me1.display( );time2=time1+; /将自加前的对象的值赋给time2couttime1+: ;time1.display( );cout time2 :;time2.display( ); /输出time2对象的值第10章 运算符重载请注意前置自增运算符“+”和后置自增运算符“+”二者作用的区别前者是先自加,返回的是修改后的对象本身后者返回的是自加前的对象,然后对象自加请仔细分析后置自增运算符重载函数运行结果如下: time1 : 34:59(time1原值)+time1: 35:0 (执行+time1后time1的值)time1+: 35:1 (再执行time1+后t
33、ime1的值)time2 : 35:0 (time2保存的是执行time1+前time1的值)可以看到: 重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用编译系统在遇到重载后置自增运算符时,会自动调用此函数第10章 运算符重载C+的流插入运算符“”是C+在类库中提供的,所有C+编译系统都在类库中提供输入流类istream和输出流类ostreamcin和cout分别是istream类和ostream类的对象在类库提供的头文件中已经对“”进行了重载,使之作为流插入运算符和流提取运算符,能用来输出和输入C+标准类型的数据因此,在本书
34、前面几章中,凡是用“cout”对标准类型数据进行输入输出的,都要用#include 把头文件包含到本程序文件中10.7 重载流插入运算符和流提取运算符第10章 运算符重载用户自己定义的类型的数据,是不能直接用“”来输出和输入的如果想用它们输出和输入自己声明的类型的数据,必须对它们重载对“”重载的函数形式如下: istream & operator (istream &,自定义类 &);ostream & operator ”的函数的第一个参数和函数的类型都必须是istream&类型,第二个参数是要进行输入操作的类重载“”和“”的函数作为友元函数或普通的函数,而不能将它们定义为成员函数第10章
35、运算符重载在程序中,人们希望能用插入运算符“”来输出用户自己声明的类的对象的信息,这就需要重载流插入运算符“”例10.7 在例10.2的基础上,用重载的“”输出复数 #include using namespace std; class Complexpublic:Complex( )real=0;imag=0;Complex(double r,double i)real=r;imag=i;Complex operator + (Complex &c2); /运算符“+”重载为成员函数friend ostream& operator (ostream&,Complex&); /运算符“”重载为
36、友元函数private:10.7.1 重载流插入运算符“”第10章 运算符重载double real;double imag;Complex Complexoperator + (Complex &c2)/定义运算符“+”重载函数return Complex(real+c2.real,imag+c2.imag);ostream& operator (ostream& output,Complex& c) /定义运算符“”重载函数output(c.real+c.imagi)endl;return output;int main( )Complex c1(2,4),c2(6,10),c3;c3=c
37、1+c2;coutc3;return 0;第10章 运算符重载(在Visual C+ 6.0环境下运行时,需将第一行改为#include ,并删去第2行)运行结果为(8+14i)可以看到在对运算符“”重载后,在程序中用“”不仅能输出标准类型数据,而且可以输出用户自己定义的类对象用“coutc3”即能以复数形式输出复数对象c3的值形式直观,可读性好,易于使用下面对怎样实现运算符重载作一些说明程序中重载了运算符“”,运算符重载函数中的形参output是ostream类对象的引用,形参名output是用户任意起的分析main函数最后第二行: coutc3;第10章 运算符重载运算符“”的左面是cou
38、t,前面已提到cout是ostream类对象“”的右面是c3,它是Complex类对象由于已将运算符“”的重载函数声明为Complex类的友元函数,编译系统把“coutc3”解释为operator(cout,c3)即以cout和c3作为实参,调用下面的operator函数:ostream& operator(ostream& output,Complex& c)output(c.real+c.imagi)endl;return output;调用函数时,形参output成为cout的引用,形参c成为c3的引用因此调用函数的过程相当于执行: cout(c3.real+c3.imagi)endl;
39、 return cout;第10章 运算符重载请注意: 上一行中的“”是C+预定义的流插入符,因为它右侧的操作数是字符串常量和double类型数据执行cout语句输出复数形式的信息然后执行return语句请思考: return output的作用是什么?回答是能连续向输出流插入信息output是ostream类的对象,它是实参cout的引用,也就是cout通过传送地址给output,使它们二者共享同一段存储单元,或者说output是cout的别名因此,return output就是return cout,将输出流cout的现状返回,即保留输出流的现状 请问返回到哪里?刚才是在执行coutc3;
40、在已知coutc3的返回值是cout的当前值如果有以下输出: 第10章 运算符重载coutc3c2;先处理coutc3,即(coutc3)c2;而执行(coutc3)得到的结果就是具有新内容的流对象cout,因此,(coutc3)c2相当于cout(新值)c2运算符“”左侧是ostream类对象cout,右侧是Complex类对象c2,则再次调用运算符“”重载函数,接着向输出流插入c2的数据现在可以理解了为什么C+规定运算符“”重载函数的第一个参数和函数的类型都必须是ostream类型的引用,就是为了返回cout的当前值以便连续输出请读者注意区分什么情况下的“”是标准类型数据的流插入符,什么情
41、况下的“”是重载的流插入符如第10章 运算符重载coutc35endl;有下划线的是调用重载的流插入符,后面两个“”不是重载的流插入符,因为它的右侧不是Complex类对象而是标准类型的数据,是用预定义的流插入符处理的还有一点要说明: 在本程序中,在Complex类中定义了运算符“”重载函数为友元函数,因此只有在输出Complex类对象时才能使用重载的运算符,对其他类型的对象是无效的如cout”的作用是从一个输入流中提取数据,如“cini;”表示从输入流中提取一个整数赋给变量i(假设已定义i为int型)重载流提取运算符的目的是希望将“”用于输入自定义类型的对象的信息10.7.2 重载流提取运算
42、符“”第10章 运算符重载例10.8 在例10.7的基础上,增加重载流提取运算符“”,用“cin”输入复数,用“cout”输出复数#include using namespace std;class Complexpublic:friend ostream& operator (ostream&,Complex&); /声明重载运算符“ (istream&,Complex&); /声明重载运算符“”private:double real;double imag;ostream& operator (ostream& output,Complex& c) /定义重载运算符“”output(c.r
43、eal+c.imag (istream& input,Complex& c) /定义重载运算符“”第10章 运算符重载coutc.realc.imag;return input;int main( )Complex c1,c2;cinc1c2; coutc1=c1endl; coutc2=c2endl;return 0;运行情况如下: input real part and imaginary part of complex number:3 6input real part and imaginary part of complex number:4 10c1=(3+6i)c2=(4+10i)第10章 运算符重载以上运
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 企业字号转让合同
- 生物制药细胞培养技师考试试卷及答案
- 私教老师课签订协议书
- 双方约定协议书版
- 农户猪栏建设方案范文
- 加工厂房建设方案
- 镇江改造工程监理协议书
- 衣物送洗管理制度
- 污水池作业安全规范施工方案
- 商业秘密泄露事件调查与应对方案
- YY/T 1992-2025采用机器人技术的辅助手术设备总结性可用性测试方法
- 广东省2026届高三下学期普通高等学校招生全国统一考试模拟测试(一)地理试卷(含答案)
- 棋牌室消防安全责任制度
- 青岛农商银行代理实物贵金属业务管理办法培训
- 国开2026年《新媒体产品策划》形考作业1-5答案
- PRP培训课件教学课件
- 医疗机构反家暴培训课件
- 《西藏自治区国省公路养护预算指标(定额)》
- 水上交通安全知识进校园题库及答案
- TCWEA19-2023水利水电工程生态护坡技术规范
- 2025四川省自然资源投资集团招聘30人笔试参考题库附带答案详解(3卷)
评论
0/150
提交评论