一、函数重载
- 概念:在同一作用域内,声明几个功能相同(或类似)的同名函数,实现功能类似但所处理数据类型不同的函数
函数重载的条件
- 函数名必须相同
- 函数的参数不同(参数个数不同或参数对应位置的数据类型不同)
- 作用域必须相同
- 函数重载与函数的返回值无关
哪些情况不构成重载、构成重载
- ①顶层const不构成重载,所以下面的两个函数不构成重载
int add(int a,int b);
int add(const int a,const int b);
- ② 普通引用不构成重载,所以下面的两个函数不构成重载
int add(int a,int b);
int add(int& a,int& b);
- ③常量引用或者常用指针构成重载,所以下面两组函数都构成重载(因为const对象不能转换为其它类型,所以只能将const对象传递给const形参。相反,非常量对象可以传递给任何类型,所以非const对象都可以传递给下面4个函数)
int add(int* a,int* b);
int add(const int* a,const int* b);int add(int& a,int& b);
int add(const int& a,const int& b);
- ④ const函数与非const函数构成重载
int add(int a,int b);
int add(int a,int b)const;
函数重载与数据类型之间的关系
- 在书写函数时,我们要确定自己的需求。如果形参的数据类型没有设置好,编译器会报错
案例一:
- 下面的add(3.1,4.25)函数参数为double类型,调用此函数时,编译器没有找到有double类型的函数,于是就去类型转换寻找适合的函数,发送double可以转换为int,也可以转换为float。于是产生二义性,程序报错
- 但是可以通过在数字后面给出F或者强制类型转换来告诉编译器具体调用哪一个函数
int add(int a,int b);
float add(float a,float b);
int main()
{
add(1,2);
add(3.1,4.25);//报错
add(3.1F, 4.25F);//正确
add((float)3.1,(float)4.25);//正确
}
- 例如:下面的3.14会转换为float也会转换为long,因此也报错
int func(long a);
int func(float a);
int main()
{
func(3.14);//报错
}案例二:
- 下面的func函数调用也会产生二义性
- 第一步:调用func时,参数1为int,此时func去匹配两个重载函数,发现int func(int a,int b);比较合适,于是就调用此函数
- 第二步:匹配到参数2时,3.14为double类型,编译器发现int func(double a,double b);比较合适,于是就调用此函数
- 最后,编译器产生二义性报错
int func(int a,int b);
int func(double a,double b);
int main()
{
func(1, 3.14); //报错
}案例三:
- 常量形参与非常量形参的调用(上面,介绍过常量形参与非常量形参形成重载)
- 执行func(a);时,因为a为非常量,因此两个函数都可以调用。但是因为把非常量赋值给常量需要强制类型转换,因此就调用int func(int &s);
- 执行func(b);时,传入的b是const类型,因此不能把普通引用绑定到const对象上,所以只调用int func(const int &s);
- 常量指针的原理也是如此(详情见const讲解文章)
int func(int &s);
int func(const int &s);
int main()
{
int a = 10;
const int b = 20;
func(a); //调用int func(int &s);
func(b); //调用int func(const int &s);
}
函数重载示例代码
struct myPoint
{
int row, col;
};
myPoint add(myPoint a, myPoint b)
{
myPoint tempPoint;
tempPoint.col = a.col + b.col;
tempPoint.row = a.row + b.row;
return tempPoint;
}
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
int main()
{
cout << add(1, 2) << endl; //3
cout << add(2.5,3.1) << endl; //5.6
return 0;
}
函数重载与作用域的关系
- 下面的代码不规范(不应该在一个函数中声明一个函数)。但是只是为了函数重载与作用域的关系
string read();
void print(const string&);
void print(double);
int main()
{
bool read = false;
int val = 1;
string s = read(); //错误,read函数与局部变量read冲突
void print(int); //定义一个参数为int的print函数
print("Value"); //错误,print(const string&);被隐藏
print(val); //正确,调用print(int)
print(3.14); //正确,调用print(int)
}
const_cast与重载
- const_cast介绍参阅
- 现在我们有下面一个这样的函数,其中参数与返回值都是const类型的:
//比较两个string对象的长度,返回较短的那个引用
const string &shortString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
- 如果我们将两个非const string对象传递给这个函数,那么返回的仍然是const string的引用
- 因此,我们希望有一种新的函数,当传入给它的实参不是const时,也得到一个非const对象的引用,那么使用const_cast可以做到这一点:
//比较两个string对象的长度,返回较短的那个引用
const string &shortString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
string &shortString(string &s1, string &s2)
{
//调用const string&类型的shortString
auto &r = shortString(const_cast<const string&>(s1),
·const_cast<const string&>(s2));
//然后将返回的结果去掉const性质
return const_cast<string&>(r);
}
二、缺省参数(默认实参)
- 概念:人为的为函数定义一个或多个默认参数,在调用函数时,它们被赋予一个相同的值
注意事项:
- 如果函数没有声明,则默认形参直接写在函数定义时
- 如果函数有声明和定义,则默认形参只可写在函数声明中,函数定义时不可以写
- 默认形参只能写在最后,或其及其后面都是默认形参
- 缺省参数是在编译阶段决定的,所以只能用常量或者全局变量作为缺省参数
- 函数调用时给出实参,会覆盖掉默认参数
缺省参数示例代码
int add(int a, int b,int c=10)
{
return a + b+c;
}
int main()
{
cout << add(1, 2) << endl; //13
cout << add(1,2,50) << endl; //53
return 0;
}
使用全局变量初始化缺省参数
- 缺省参数可以使用一个函数外的变量/常量初始化。但是不能用函数内的局部变量初始化
int a=10;
int b=20;
char c='a';
void func(int num1=a,int num2=b,char s=c);
- 注意事项:虽然可以用全局变量初始化缺省参数,但是如果全局变量被改变了,再次调用这个函数缺省参数的值也会改变
int a=10;
int b=20;
char c='a';
void func2()
{
a=66; //改变全局变量
int b=30; //局部变量,但是不影响全局变量
func(); //调用func(66,20,'a');
}
函数的调用
- 原理:使用的函数逐个入栈,main函数中调用到某函数时,跳到栈对应的函数位置,并返回结果
- 普通函数的调用与内联函数的内存模型
三、函数重载冲突
- 函数重载与缺省参数冲突的特殊情况
int add(int a, int b,int c=10)
{
return a + b+c;
}
int add(int a, int b)
{
return a + b;
}
int main()
{
cout << add(1, 2) << endl;//报错,对函数的调用不明确
return 0;
}