当前位置:   article > 正文

C++函数重载,引用,auto

C++函数重载,引用,auto

目录

函数重载

函数默认参数(C语言不支持):

函数重载概述:

函数重载的原理--名字修饰(name Mangling):

引用

概述

auto关键字

基于范围的for循环


函数重载

自然语言中,一个词可以有多重含义,可以通过上下文来判断该词真是的含义,即该词被重载了。

函数默认参数(C语言不支持):

默认参数的概念,是声明或定义函数时为函数的参数赋值,在调用该函数时,如果没有指定实参则采用默认形参来替代,否则使用指定的实参,默认参数的值必须是全局变量或者常量。如下:

  1. #include<iostream>
  2. using namespace std;
  3. void Func(int a = 0) {
  4. cout << a << endl;
  5. }
  6. int main() {
  7. Func();
  8. Func(10);
  9. }

还可以把函数的全部参数变为默认参数,如下:

  1. #include<iostream>
  2. using namespace std;
  3. void Func(int a = 0) {
  4. cout << a << endl;
  5. }
  6. void Func1(int a = 0, int b = 20, int c = 30) {
  7. cout << "a=" << a << endl;
  8. cout << "b=" << b << endl;
  9. cout << "c=" << c << endl;
  10. }
  11. int main() {
  12. Func1();
  13. cout << "第二此打印:" << endl;
  14. Func1(10);
  15. }

可以看到如果只传一个参数那么只会把第一个函数的第一个代替,因为他们是一一代替的,所以如果是c没有默认参数那么要传三个参数,第三个才能接收到。

而且默认参数必须是从右往左连续的的,如果不是那会报错,如下所示:

如:void Func1(int a=10,int b=20,int c=30);  有四种调用方式,如下:

Func1();

Func1(0);

Func1(2,1);

Func1(3,1,2);

因为函数是一个一个的接收按照顺序来放置传进来的参数的,所以不能这种:Func1(,2);这样是传不到第二个参数哪里的,因为你传几个参数它才会接收到第几个参数会报错,这样只传了写了第二个的值的第一个为空所以无法接收。

如果函数有默认参数,声明和定分离的时候只能在声明的地方给默认参数。

函数重载概述:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。一定要函数名相同,参数不同(参数个数,或者是类型)才能实现重载函数类型不算。如下实现数值相加的函数重载:

  1. #include<iostream>
  2. using namespace std;
  3. int Add(int left, int right) {
  4. cout << "int Add(int left,int right)" << endl;
  5. return left + right;
  6. }
  7. double Add(double left, double right) {
  8. cout << "double Add(double left,double right)" << endl;
  9. return left + right;
  10. }
  11. int main() {
  12. cout <<"整数的和:" << Add(1, 2) << endl;
  13. cout << "小数的和:" << Add(2.3, 23.1) << endl;
  14. return 0;
  15. }

上述实现整数,小数相加的操作,Add(1,2);传的两个参数都是整数,刚好跟int Add(int left,int right);的参数相匹配所以就是调用一个Add,同理 Add(2.3, 23.1);参数与第二个Add相匹配也是直接调用第二个Add,代码运行结果如下图:

可以看到当函数名相同时调用哪个函数取决于传递的参数。

如果我传Add(1,2.3);一个整形一个小数,那么它会直接报错,因为我们对Add使用了函数重载那么它会就去找那一个传进去的参数和函数参数一样的,如果找不到则证明没有,但是我们知道整数和小数之间是可以隐式转换的,但是上述代码它该转成那一个呢编译不知道会直接报错。如果代码中只有两个函数的其中一个它会自动转换的不会报错。

函数重载时里面也可以有默认参数的,并不会影响函数重载,因为函数重载只看参数的类型不看默认参数的。

函数返回值不能构成重载的原因是,如果返回值可以重载那么两个参数一样但是放回值不同的函数那应该调用那一个呢,很显然编译器并不知道,所以返回值不能构成重载。

函数重载的原理--名字修饰(name Mangling):

在C/C++中,一个程序要运行起来,需要经历几个阶段:预处理,编译,汇编,链接。

我们知道在预处理阶段会把头文件展开(意味着把头文件的内容拷贝到展开的位置)/宏替换/条件编译/去掉注释...这就生成了一个文件名.i后缀的文件,在编译阶段会检查代码的语法并生成汇编代码然后生成了文件名.s后缀的文件,汇编阶段把代码转换成二进制的机器码然后生成了文件名.o后缀的文件,链接的阶段是把所有文件的代码合并在一起,链接一些没有确定函数地址等(函数之后在调用时才会创建,然后才会有对应的地址)生成文件名.out文件。

C语言没有重载的原因是:C语言在链接函数地址时,用函数去找。(C不存在同名函数,因为它找函数使用函数名去找的),而C++是把参数代入进去修饰了,然后使用修饰之后的函数名去找的。从下图的C++程序中可以看到,如函数名相同但是参数不同链接时生成的函数名也不同,所以C++可以重载因,为他们链接时函数名就不同了,Windows下修饰的函数名:

引用

概述

引用不是新定义一个变量,而是给已经存在的变量取一个别名,编译器不会为了引用变量开辟内存空间,它和它引用的变量公用同一块内存空间。如李白被称为诗仙。李白和诗仙都是同一个人。

语法:

类型& 引用变量名(对象名)=引用实体;

特性:

引用在定义时必须初始化

一个变量可以有多个引用

引用一旦引用一个实体,再不能引用其他实体

示例:

  1. void TestRef(){
  2. int a=10;
  3. int &ra=a;//定义引用类型引用a
  4. printf("%p\n",&a);
  5. printf("%p\n",&ra);
  6. }

需要注意的是,引用类型必须和引用实体是同种类型的。,他们之间的关系图如下:

可以看到他们都可以使用同一块内存空间,而且使用方法都是相同的,而且a可以取多个别名,相比指针少了一些更麻烦的操作。

引用可以用来做函数参数代码如下:

  1. void Swap(int &left,int &right)
  2. {
  3. int temp=left;
  4. left=right;
  5. right=temp;
  6. }
  7. int main(){
  8. int a=3,b=7;
  9. Swap(a,b);
  10. return 0;
  11. }

这样不需要传地址也能交换两个实参的值,而且使用起来可以减少很多麻烦。引用和指针的区别,在语法概念引用就是一个别名,没有独立的空间,和其引用的实体公用同一块空间。因为引用不能更改而指针可以改所以引用不能进行比如逐个访问数据这种操作。所以引用很大程度上面代替不了指针。因为引用和核心是用来做参数,返回值的。函数的形参改变实参这时形参就像实参的别名,所以可以使用引用来减少代码量完成同样的工作,而且数值量比较大的时候使用引用能提高效率。

auto关键字

auto可以自动推导类型,比如把整形的a变量赋值给它定义的变量他就是整形,我们可以通过typeid来打印数据的类型,如下代码:

  1. int main() {
  2. int a = 0;
  3. auto c = a;
  4. auto d = &a;
  5. auto* e = &a;
  6. auto& f = a;
  7. f++;
  8. //typeid打印类型
  9. cout << typeid(c).name() << endl;
  10. cout << typeid(d).name() << endl;
  11. cout << typeid(e).name() << endl;
  12. cout << typeid(f).name() << endl;
  13. return 0;
  14. }

运行结果为:

  • int
  • int *
  • int *
  • int 

由上可以看到auto定义的c是赋值变量a的类型,而d变量则为指针所以它才能接收&a,也可以自己写*把其定义为指针类型,还可以&把定义变量改为引用类型。


随着程序越来越复杂,程序中用到的类型也越来越复杂,导致类型难于拼写,含义不明导致容易出错,如:

std::map<std::string,std::string>::iterator是一个类型,但是类型太长,容易写错。

所以auto的价值主要还是用来替代较长类型的定义。如:

vector<string>::iterator it=v.begin();

auto it=v.begin();

用auto声明指针类型时,用auto和auto*并无区别,但用auto声明引用类型时必须加&

auto在实际中最常见的优势就是跟C++11提供的for循环,还有lambda表达式进行配合使用。


 使用auto时需要注意的是:

  • 在同一行定义多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数参数。
  • auto不能用来声明数组。
  • 在C++11中只保留了auto作为类型指示符的用法

基于范围的for循环

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错。所以在C++11中引入for基于范围的for循环。


for循环后的括号由冒号":"分为两部分:第一部分是范围内用于迭代的变量,第二部分是表示被迭代的范围。代码如下:

  1. void TestFor()
  2. {
  3. int array[]={1,2,3,4,5};
  4. for(auto&e:array)
  5. e*=2;//将array里每个元素乘2
  6. for(auto e:array)
  7. cout<<e<<".";
  8. }

与正常的for循环并无区别,可以使用continue来结束循环,也可以用break来跳出循环。

for循环迭代的范围必须是确定的:对于数组而言,就是数组中第一个元素和最后一个元素的范围,对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围。


对于下面没有明确数组的范围,这种代码就有错:

void TestFor(int array[]){

        for(auto&e:array)

        cout<<e<<endl;

}

C++类和对象基础-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/lh11223326/article/details/136834917?spm=1001.2014.3001.5501

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/330436
推荐阅读
相关标签
  

闽ICP备14008679号