付出,终有回报!
分类: C/C++
2017-10-21 21:49:27
这两个问题是任何支持函数重载的语言都必须要解决的问题!带着这两个问题,我们开始本文的探讨。本文的主要内容如下:
看下面的一个例子,来体会一下:实现一个打印函数,既可以打印int型、也可以打印字符串型。在C++中,我们可以这样做:
#includeusing namespace std; void print(int i) { cout<<"print a integer :"<void print(string str) { cout<<"print a string :"< int main() { print(12); print("hello world!"); return 0; }
通过上面代码的实现,可以根据具体的print()的参数去调用print(int)还是print(string)。
既然返回类型也考虑到映射机制中,这样不同的返回类型映射之后的函数名肯定不一样了,但为什么不将函数返回类型考虑到函数重载中呢?
——这是为了保持解析操作符或函数调用时,独立于上下文(不依赖于上下文),看下面的例子
float sqrt(float);
double sqrt(double);
void f(double da, float fla) {
float fl=sqrt(da);//调用sqrt(double)
double d=sqrt(da);//调用sqrt(double)
fl=sqrt(fla);//调用sqrt(float)
d=sqrt(fla);//调用sqrt(float)
}
如果返回类型考虑到函数重载中,这样将不可能再独立于上下文决定调用哪个函数。
现在已经解决了重载函数命名冲突的问题,在定义完重载函数之后,用函数名调用的时候是如何去解析的?为了估计哪个重载函数最适合,需要依次按照下列规则来判断:
如果在最高层有多个匹配函数找到,调用将被拒绝(因为有歧义、模凌两可)。看下面的例子:
void print(int);
void print(const char*);
void print(double);
void print(long);
void print(char);
void h(char c,int i,short s, float f) { print(c);//精确匹配,调用print(char)
print(i);//精确匹配,调用print(int)
print(s);//整数提升,调用print(int)
print(f);//float到double的提升,调用print(double)
print('a');//精确匹配,调用print(char)
print(49);//精确匹配,调用print(int)
print(0);//精确匹配,调用print(int)
print("a");//精确匹配,调用print(const char*)
}
定义太少或太多的重载函数,都有可能导致模凌两可,看下面的一个例子:
void f1(char);
void f1(long);
void f2(char*);
void f2(int*);
void k(int i) { f1(i);//调用f1(char)? f1(long)?
f2(0);//调用f2(char*)?f2(int*)?
}
这时侯编译器就会报错,将错误抛给用户自己来处理:通过显示类型转换来调用等等(如f2(static_cast
int pow(int ,int);
double pow(double,double);
void g()
{
double d=pow(2.0,2)//调用pow(int(2.0),2)? pow(2.0,double(2))?
}
那么,为什么非引用类型参数的常量性不同不能重载呢?C++的这样设计的道理何在?这还得从C语言说起,C++出于与C语言保持兼容的考虑,保留了C语言中的传值调用(参考Python、Java这些面向对象语言,他们根本没有这个概念,从C++的角度来看它们的函数调用都是引用传参)。而在传值调用过程中,由于函数内部总会在执行函数体之前复制一个实参副本,并在函数体内使用副本参与运算。这就导致实参本身(实参为指针时,指针所指对象就不一定不改变了。)在函数内部永远不会改变,函数返回时实参的值总是调用时实参的值。既然如此,那么实参是否为const类型根本无关紧要,即使不是const类型函数返回时也一定不会变。
相反地,引用和指针所指对象的常量性则显得很重要了!因为这些对象在函数内部是有可能被改变的。
总而言之,判断一组同名函数能否构成重载,要看能不能通过修改形参来影响实参的值,如果两个函数实参的值都能被修改或都不能被修改,则不能构成重载;否则构成重载。