C++Primer第五版学习(函数部分 四)函数重载

今天学习的是C++Primer第五版学习(函数部分 四)函数重载,欢迎关注我的公众号:Cpp编程小茶馆,一起学习交流。
函数重载

1. 定义重载函数

2. 重载和const形参

3 顶层const

4. 调用重载的函数

函数重载

如果同一作用域内的几个函数名字相同但形参列表不同,我们称之为重载(overloaded)函数。

1. 定义重载函数

那么,如何定义重载函数呢?假设:有一种典型的数据库应用,需要创建几个不同的函数分别根据名字、电话、账户号码等信息查找记录。函数重载使得我们可以定义一组函数,它们的名字都是lookup,但是查找的依据不同。我们能通过以下形式中的任意一种调用lookup函数:

Record lookup (const Account&) ;//根据Account查找记录
Record lookup (const Phone&) ;//根据Phone查找记录
Record lookup (const Name&);//根据Name查找记录
Account acct;
Phone phone;
Record rl = lookup (acct);//调用接受Account的版本
Record r2 = lookup (phone);  //调用接受Phone的版本

其中,虽然我们定义的三个函数各不相同,但它们都有同一个名字。编译器根据实参的类型确定应该调用哪一个函数。对于重载的函数来说,它们应该在形参数量或形参类型上有所不同。在上面的代码中,虽然每个函数都只接受一个参数,但是参数的类型不同。不允许两个函数除了返回类型外其他所有的要素都相同。假设有两个函数,它们的形参列表一样但是返回类型不同,则第二个函数的声明是错误的:

Record lookup (const Account&);
bool lookup (const Account&); //错误:与上一个函数相比只有返回类型不同

比只有返回类型不同

2. 重载和const形参

前面学习过,顶层const 不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:

Record lookup (Phone);
Record lookup (const Phone);//重复声明了 Record lookup (Phone)

Record lookup (Phone*) ;
Record lookup (Phone* const); // 重复声明了 Record lookup (Phone*)

在这两组函数声明中,每一组的第二个声明和第一个声明是等价的。另一方面,如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的:

//对于接受引用或指针的函数来说,对象是常量还是非常量对应的形参不同
//定义了4个独立的重载函数
Record lookup (Account&);         //函数作用于Account的引用
Record lookup (const Account&); //新函数,作用于常量引用

Record lookup (Account*) ;        //新函数,作用于指向Account的指针
Record lookup (const Account*);     //新函数,作用于指向常量的指针

在上面的例子中,编译器可以通过实参是否是常量来推断应该调用哪个函数。

因为const不能转换成其他类型,所以我们只能把const对象(或指向const的指针)传递给const形参。相反的,因为非常量可以转换成const,所以上面的4个函数都能作用于非常量对象或者指向非常量对象的指针。

不过,当我们传递一个非常量对象或者指向非常量对象的指针时,编译器会优先选用非常量版本的函数。

补充学习以下顶层const

3 顶层const

如前所述,指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const(top-level const)表示指针本身是个常量,而用名词底层const (low-level const)表示指针所指的对象是一个常量。更一般的,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。

底层const则与指针和引用等复合类型的基本类型部分有关。

比较特殊的是,指针类型既可以是顶层const也可以是底层const,这一点和其他类型相比区别明显:

int i= 0
int *const pl = &i;  //不能改变p1的值,这是一个顶层const
const int ci = 42; //不能改变ci的值,这是一个顶层const
const int *p2 = &ci; //允许改变p2的值,,这是一个底层const
const int *const p3 = p2; //靠右的const 是顶层 const,靠左的是底层const
const int &r = ci; //用于声明引用的const都是底层const

当执行对象的拷贝操作时,是顶层const还是底层const区别明显。其中,顶

层const不受什么影响:

i= ci; //正确:拷贝ci的值, ci是一个顶层const,对此操作无影响
p2 = p3; //正确: p2和p3指向的对象类型相同, p3顶层const的部分不影响

执行拷贝操作并不会改变被拷贝对象的值,因此,拷入和拷出的对象是否是常量都没什么影响。

另一方面,底层 const的限制却不能忽视。当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。

一般来说,非常量可以转换成常量,反之则不行:

int *p =p3  //错误: p3包含底层const的定义,而p没有
p2 = p3; //正确: p2和p3都是底层const
p2 = &i; //正确: int*能转换成const int*
int &r= ci; //错误:普通的int&不能绑定到int常量上
const int &r2 = i; // 正确 const int&可以绑定到一个普通int上

p3既是顶层const也是底层const,拷贝p3时可以不在乎它是一个顶层const,但是必须清楚它指向的对象得是一个常量。因此,不能用p3去初始化p,因为p指向的是一个普通的(非常量)整数。

另一方面,p3的值可以赋给p2,是因为这两个指针都是底层const,尽管p3同时也是一个常量指针(顶层const),仅就这次赋值而言不会有什么影响。

4. 调用重载的函数

定义了一组重载函数后,我们需要以合理的实参调用它们。函数匹配(functionmatching)是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫做重载确定(overload resolution)。

编译器首先将调用的实参与重载集合中每一个函数的形参进行比较,然后根据比较的结果决定到底调用哪个函数。在很多(可能是大多数)情况下,程序员很容易判断某次调用是否合法,以及当调用合法时应该调用哪个函数。

通常,重载集中的函数区别明显,它们要不然是参数的数量不同,要不就是参数类型毫无关系。此时,确定调用哪个函数比较容易。

但是在另外一些情况下要想选择函数就比较困难了,比如当两个重载函数参数数量相同且参数类型可以相互转换时,我们将在后面文章继续学习当函数调用存在类型转换时编译器处理的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cpp编程小茶馆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值