函数多态时c++在c语言的基础上新增的功能。默认参数让我们能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)让我们能够使用多个同名的函数 。术语“多态”指的是有多种形式,因此函数多态允许函数可以有多个形式。类似地,术语“函数重载”指的是可以有多个同名的函数,因此对名称进行了重载。这两个术语指的是同一回事,但我们通常使用函数重载。可以通过函数重载来设计一系列函数----它们完成相同的工作,但是用不同的参数列表。
1、概念
用相同的函数名定义多个不同的功能称为函数重载。重载的函数根据参数的个数和类型进行区分,但不能单独根据返回类型进行区分。函数重载的关键是函数的参数列表(函数特征标)。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关重要的。c++允许定义名称相同的函数,条件是它们的特征标不同。如果参数数目或参数列表不同,则特征标也不同。
2、规则
函数重载的规则:
1、函数名称必须相同
2、参数列表(函数特征标)必须不同(个数不同,类型不同,参数排列顺序不同等)
3、函数的返回类型可以相同也可以不同
4、仅仅返回类型不同不足以称为函数重载的重载
5、需要在同一作用域下
//swap_reload.cpp
#include <iostream>
using namespace std;
void swap_val(int *a,int *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
cout<< "swap_val(int*,int*)" << *a << " " << *b << endl;
}
// void swap_val(int *b,int *a) //error 不能够重载
// {
// *a = *a + *b;
// *b = *a - *b;
// *a = *a - *b;
// cout<< "swap_val(int*,int*)" << *a << " " << *b << endl;
// }
void swap_val(float *a,float *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
cout<< "swap_val(float*,float*)" << *a << " " << *b << endl;
}
void swap_val(char *a,char *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
cout<< "swap_val(char*,char*)" << *a << " " << *b << endl;
}
void swap_val(bool *a,bool *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
cout<< "swap_val(bool*,bool*)" << *a << " " << *b << endl;
}
int main()
{
int a=3,b=4;
swap_val(&a,&b); //根据 ab的数据类型来决定调用哪一个函数
//cout<<a<<" "<<b<<endl;
float a1=3.5,b1=4.5;
swap_val(&a1,&b1); //根据 ab的数据类型来决定调用哪一个函数
//cout<<a1<<" "<<b1<<endl;
bool a2 = true,b2 = false;
swap_val(&a2,&b2); //根据 ab的数据类型来决定调用哪一个函数
//cout<<a2<<" "<<b2<<endl;
char a3='a',b3='b';
swap_val(&a3,&b3); //根据 ab的数据类型来决定调用哪一个函数
//cout<<a3<<" "<<b3<<endl;
return 0;
}
3、作用
函数重载的作用主要是两点:
1、解决函数名字资源问题
2、函数调用的时候很方便,自动根据不同的函数调用不同函数(静态多态-编译时候多态)
4、问题
当默认参数和函数重载遇到会发生事情呢?
#include<iostream>
using namespace std;
//将void test()放在命名空间就不会产生歧义
// namespace spaceA
// {
// void test()
// {
// }
// }
void test()
{
}
void test(int x=10)
{
}
int main()
{
// test(11);//调用 的是:void test(int x=10)
test();//调用的是???歧义
}
当函数没有参数列表和函数全部参数都有默认参数的时候,当cpp文件中都存在上述两种函数声明或定义的时候,当我们调用上述两种函数之一的时候,编译器会给我们报错,因为两种函数都符合我们的需求,这里就存在函数二义性,编译器不知道我们需求的是哪一个函数,从而程序报错。
解决方法也很简单,把函数定义或声明放到命名空间内,然后通过命名空间来调用指定函数。
4、命名空间(名称空间)
c++新增了这样一种功能,即通过定义一种新的声明区域来创建命名的名称空间,这样做的目的之一是提供一个声明名称空间的区域。一个名称空间中的名称不会与另外一个名称空间的相同名称冲突,同时允许程序的其他部分使用该名称空间中声明的东西。
命名空间创建的格式
namespace 名称空间名字
{
//声明定义变量
//声明定义函数
//定义结构体/共用体/枚举 数据类型
//声明类 定义对象
//可嵌套定义
//namespace 名称空间名字
}
名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。因此,在默认情况下,在名称空间中声明的名称的链接性为外部的(其他文件也可以使用)(除非它引用了常量)
当然,当我们定义了一个名称空间之后,需要有一种方法来访问给定名称空间中的名称,最简单的方法就是通过作用域解析运算符(::),使用名称空间来限定该名称;
namespace Jack{...}
Jack::pail = 12.34 //use a variable
Jack::swap(); //use a function
Jack::Jim::a = 12 //create a namespace
未被装饰的名称(pail)称为未限定的名称,包含名称空间的名称(Jack::pail)称为限定的名称
5、名称空间的特性
1、using声明和using编译指令
using声明使特定的标识符可用,using编译指令使整个名称空间可用。
using声明由被限定的名称和它前面的关键字using组成,using声明将特定的名称添加到它所属的声明区域中。using声明式一个名称可用,而using编译指令使所有的名称都可用。using编译指令由名称空间名和它的前面的关键字using namespace组成,它使名称空间中所有名称都可用,而不需要使用作用域解析运算符,在全局声明区域中使用using编译指令,将使该名称空间的名称全局可用。这种情况已经出现过多次:
#include
using namespace std; //using编译指令,使该cpp文件可以访问std这个名称空间
有关using编译指令和using声明,需要记住一点是,它们增加了名称冲突的可能性。也就是说,如果由名称空间a和b,并在代码中使用作用域解析运算符,则不会存在二义性。
但事实上,编译器不允许我们同时使用不同名称空间同一变量名称的using声明,因为这将导致二义性。
如果函数被重载,则一个using声明将导入所有的版本
2、using编译指令与using声明比较
1、using声明可以单独使用名称空间中某个特定的名称,其作用域与using所在的声明区域相同。using编译指令使名称空间的所有名称可用,如果在全局中使用using编译指令,将使该名称空间中的名称在全局可用,如果在函数定义中使用using编译指令,将会在该函数中该名称空间可用
2、在名称冲突的时候,两者也会有部分差异;例如,名称空间和using声明的区域存在相同的名称,如果在该区域中使用using声明导入名称,则两个名称会发生冲突而出错。如果使用using编译指令,则该区域的局部版本将会隐藏名称空间的版本。using声明的使用比using编译指令要更加安全
(声明会产生冲突(二义性),编译会发生隐藏)
3、名称空间的其他属性
可以将名称空间声明进行嵌套
namespace a
{
namespace b;
}
也可以在名称空间中使用using声明与using编译指令
namespace a
{
using std::cout;
using namespace c;
}
可以给名称空间创建别名
namespace my_very_favorite_things{...};
namespace mvft = namespace my_very_favorite_things;
可以使用这种技术来简化对嵌套名称空间的使用
4、未命名的名称空间
namespace
{
int a;
int b;
}
这就像后面跟着using编译指令一样,也就是说,在该名称空间中声明的名称的潜在作用域为:从声明点到该声明区域末尾。从这个方面看,它们与全局变量相似。然而,由于这种名称空间没有名称,因此不能显示的使用using声明和using编译指令来使它在其他位置都可用。具体的说,不能再未命名名称空间所属文件之外的其他文件中,使用该名称空间的名称。这提供了链接性为内部的静态变量的替代品(默认只能在该文件内使用)
6、名称空间用途
1、如果在已命名的名称空间中声明的变量,而不是使用外部全局变量
2、如果在已命名的名称空间中声明的变量,而不是使用静态全局变量
3、如果开发了一个函数库和类库,将其放在一个名称空间中
4、仅将便一直将using作为一种将旧代码转换为使用名称空间的权宜之计
5、不要在头文件中使用using编译指令
6、导入名称时,首选使用作用域解析运算符或using声明的方法
7、对于using声明,首选将其作用域设置为局部而不是全局