第六章------重载函数
这里写目录标题
定义:两个函数名字相同,并且在相同的域中被声明,但参数表不同。
1、重载函数声明
-
怎么重载一个函数名
-
若两个函数的参数表中参数的个数或类型不同,则认为这两个参数是重载的。
//例 void print(const string &); void print(vector<int> &);
-
若两个函数的返回类型和参数表精确匹配,则第二个声明视为第一个的重复声明。
//例 void print(const string &str); void print(const string &); //参数表的比较过程与参数名无关
-
若两个函数的参数表相同,但是返回类型不同,则第二个声明视为第一个的错误重复声明。
//例 unsigned int max(int i1,int i2); int max(int,int);//错误 //函数的返回类型不足以区分两个重载函数
-
若在两个函数的参数表中,只有缺省实参不同,则第二个声明视为第一个声明的重复声明。
//例 int max(int *ia,int sz); int max(int *,int sz=10);//重复声明
-
当一个参数类型是const或volatile时,在识别函数声明是否相同时,并不考虑const和volatile修饰符。
//例 void f(int); void f(const int);//重复声明 //任何int的实参都可以被用来调用函数f(const)
-
若把const或volatile应用在指针或引用参数指向的类型上,则在判断函数声明是否相同时,就要考虑const和volatile修饰符。
//例 void f(int*); void f(const int*);//声明了不同的函数 /*-------------------------------------*/ void f(int&); void f(volatile int&);//声明了不同的函数
-
-
重载和域
-
如果using声明向一个域中引入了一个函数,而该域中已经存在一个同名的函数,那么由using声明引入的函数重载了在该声明所出现的域中同名函数的其他声明。(using指示符类似)
//例 #include <string> namespace libs_t_us{ extern void print(int); extern void print(double); } extern void print(const string &); using libs_t_us::print;//名字空间中的两个print函数重载print(const string &)
-
如果using声明向一个域中引入了一个函数,而该域中已经存在一个同名的函数且含有相同的参数表,则该声明是错误的。
-
在嵌套的域中被声明的函数隐藏了而不是重载了外围域中的同名函数。
-
-
extern “C”和重载函数
- 链接指示符只能指定重载函数集中的一个函数。
2、重载解析的三个步骤:
- 确定函数调用考虑的重载函数的集合,确定函数调用中实参表的属性。
- 从重载函数集合中选择函数,该函数可以在(给出实参个数和类型)的情况下用调用中指定的实参进行调用。
- 选择与调用最匹配的函数。
3、参数类型转换
在函数重载解析的第二步中,编译器将函数调用的实参划分等级如下:
-
精确匹配:实参与函数参数的类型精确匹配。实参产生如下的转换也属于精确匹配:
-
从左值到右值的转换;
//例 #include <string> string color={"purple"};//color是一个左值,color里面的值“purple”是一个右值 void print(string); int main() { print(color);//调用函数时,实参是按值传递的,所以实参是从左值color到右值“purple” return 0; }
-
从数组到指针的转换;
-
从函数到指针的转换;
-
限定修饰转换。(只影响指针)
注:精确匹配可以用一个显式强制转换强行执行。如下:
extern void ff(int); extern void ff(void *); ff(0xffbc);//调用ff(int),因为0xffbc属于十六进制的int型常量。 ff(reinterpret_cast<void *> (0xffbc));//调用ff(void *);
-
-
与一个类型转换匹配:实参不直接与参数匹配,但它能转换成这样的类型。可能的转换有三种,等级由高到低为:
- 提升
- char,unsigned char或short型的实参被提升为int型。
- float型的实参被提升为double型。
- 枚举类型的实参被提升到下列第一个能够表示其所有枚举常量的类型:int,unsigned int,long,unsigned long。
- 布尔类型的实参被提升为int型。
- 标准转换:所有标准转换都被视为是等价的,没有转换优先级高的说法
- 整值类型转换、浮点转换、浮点—整值转换、指针转换、bool转换。
- 用户定义的转换(由转换函数执行)
- 提升
-
无匹配:实参不能与声明的函数的参数匹配。
-
引用:当实参是一个引用时,该实参是一个左值,它的类型是引用所指的对象的类型。
实参与引用参数的匹配结果有一下两种可能:
-
实参是引用参数的合适的初始值时,实参是参数的精确匹配。
//例 void swap(int &,int &); int manip(int i1,int i2) { swap(i1,i2);//OK return 0; }
-
实参不能初始化引用参数时,该实参不能被用来调用函数。
//例 int obj; void frd(double &); int main() { frd(obj);//错误,参数必须是const double &;实参是int型,必须转换成double才可以匹配引用参数类型,但是该转换的结果是一个临时值,只能匹配const型的参数. //若声明为void frd (const double &);则该函数调用正确。 return 0; }
注:实参是临时值的话,参数只能用const型的引用。
-
第七章-------函数模板
1、模板函数的定义
-
定义:关键字template放在模板的定义和声明的最前面,其后是用逗号分隔的 模板参数表(用尖括号<>括起来),该列表是模板参数表,不能为空;模板参数 可以是一个模板类型参数,也可以是模板非类型参数。
-
模板类型参数:由关键字class或typename后加一个标识符构成,该标识符是模板参数名。模板参数名在后面的出现中表示一个潜在的内置或用户定义的类型。
//例 template <class Type>//Type为模板参数名,该参数名可以是自己选择的任何名字 Type min(Type a,Type b)//Type表示后面用户调用该函数的的实参的类型 { return a < b ? a : b; }
-
模板非类型参数:由一个普通的参数声明构成;模板非类型参数标书该参数名代表了一个潜在的值,而该值代表了模板定义中的一个常量。如下
template <class Type,int size>//size是一个模板非类型参数,代表arr指向的数组的长度 Type min(Type (&arr) [size]); //将参数声明为数组的引用时,用户必须传递第二个实参指定数组的长度
-
-
函数定义或声明跟在模板参数表后。
//例 template <class Type,int size> Type min(Type (&r_array) [size]) { /*找到数组中最小值的参数化参数*/ Type min_val=r_array[0]; for(int i=1;i<size;i++) if(min_val>r_array[i]) min_val=r_array[i]; return min_val; }
-
函数模板也可以被声明为inline或extern,但指示符应放在模板参数表后面。
//例 template <typename Type> inline Type min(Type,Type);
2、显式模板实参
-
在调用模板函数时,模板实参被显式指定在逗号分隔的列表中,用尖括号括起来,紧跟在函数模板实例的名字后面。
//例1 template <class T> T min(T,T); {/*.......*/} /*----------调用-----------*/ unsigned int ui; min<unsigned int>(ui,1024);//显式指定时根据定义的模板函数的参数类型确定需要几个,用逗号分开 //例2 template <class T1,typename T2,class T3> T1 sum(T2,T3);//T1用来确定返回值类型,我们可以用sum 来计算任何两个不同类型的对象的和 {/*............*/} /*----------调用--------------*/ unsigned int ui; char uc; unsigned int loc=sum<unsigned int,unsigned int,char>(ui,uc);//尖括号中的参数顺序指向T1,T2,T3类型
3、模板编译模式
-
包含编译模式:
- 将模板函数的定义放在头文件中,需要使用时只需要包含该头文件即可。
-
分离编译模式:
-
在定义模板函数时,在关键字template之前加上关键字export来声明一个可导出的函数模板,定义之后,我们只要在使用前声明该模板,就可以在任意程序文本文件中使用该模板。
//mode12.h //分离模式:只提供模板声明 template <class Type> Type min(Type t1,Type t2); //mode12.c //模板定义 export template <class Type> Type min(Type t1,Type t2) {/*............*/} //user.c //用户的程序文件 #include "mode12.h" int i,j; double d=min(i,j);
-
4、模板显式特化
-
定义:显式关键字template和一对尖括号,然后是函数模板名、被用来特化模板的实参,以及函数参数表和函数体。
//例 //通用的模板定义 template <class Type> Type max(Type t1,Type t2) { return (t1>t2?t1:t2); } //为max(const char*,const char*)定义一个显式特化 typedef const char *PCC; template<> PCC max<PCC>(PCC s1,PCC s2) { return (strcmp(s1,s2)>0?s1:s2);//strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。 } //后面的调用中,对于所有用两个const char*型实参进行调用的max(),都会调用这个特化的定义。 int mainz() { int i=max(10,5);//调用通用定义模板 const char *p=max("HELLO","world");//调用特化的定义 }
-
显式特化必须声明或定义在使用之前;若先使用再声明或定义的话,由于第一次使用时调用的是通用模板,之后的调用就会报错。所以最好是将 显式特化定义或者声明 与 该通用模板函数 放在一起。
第八章--------异常处理
1、抛出异常:
- 抛出异常可通过throw表达式来实现,throw表达式由关键字 throw 后面跟一个表达式构成,该表达式的类型是被抛出异常的类型。
- 异常是一个对象,throw抛出的必须是一个class类型的对象,而不能只是一个类型。
2、try块:
-
try块必须包围能够抛出异常的语句。
-
try块以关键字 try 开始,后面是花括号括起来的语句序列;在try块之后是一组处理代码,被称为catch子句。
-
try块把语句分成组,并将其与相应的处理这些语句可能抛出的异常的处理语句相关联。
-
程序的控制流:
- 如果没有异常发生,则执行try块中的代码,和try块相关联的catch处理代码被忽略。
- 如果在for循环的第一个if语句中调用的成员函数push()抛出一个异常,则for循环的第二个和第三个if语句被忽略,该for循环和try块被退出,执行pushOnFull类型异常的处理代码。
- 如果在for循环的第三个if语句中调用的成员函数pop()抛出一个异常,则针对display()的调用被忽略,for循环和try块被退出,执行popOnEmpty类型异常的处理代码。
注:若某条语句抛出异常,而没有catch语句能够处理该异常,则程序执行权被转交给C++标准库中定义的函数terminate()。
-
一个try块引入一个局部域,在try块内声明的变量不能再try块外被引用,包括在catch子句中。
//for循环50次,将3的倍数压入栈,遇到4的倍数则显示栈的内容,遇到10的倍数时就弹出最后一项并显示栈中内容
try{ //栈的压入和弹出的操作,相应的异常处理
for (int ix=1;ix<51;ix++)
{
if(ix % 3 == 0)
stack.push( ix );
if( ix % 4 == 0)
stack.display();
if( ix % 10 == 0)
{
int dummy;
stack.pop(dummy);
stack.display();
}
}
}
catch (pushOnFull){...}
catch (popOnEmpty){...}//注:处理异常的代码放在catch子句的复合语句中。
3、捕获异常
- C++异常处理代码是catch子句。
- 一个catch子句由三部分构成:关键字catch、在括号中的单个类型或单个对象声明、复合语句中的一组语句。