• 函数重载
  • 引用
  • 内联函数
  • auto
  • 范围for循环

函数重载

C++中引入了一个新特性,函数重载。

在同一个作用域下,对于相同的函数名,函数的参数不同,不同类型的参数顺序不同,参数的个数不同,都可以形成函数的重载(参数名不同,返回值不同不形成重载)

函数的重载主要用于处理功能相同,类型不同的数据。

例如

int test(int i, int j)
{cout << "test" << endl;
}int test(double i, int j)
{cout << "test" << endl;
}int test(double i, int j, int k)
{cout << "test" << endl;
}

为什么C++支持重载,C语言不支持呢?

因为windows的处理更加复杂,所以这里用linux下的gcc和g++来看更加直观。

首先我们要知道,链接器看到有函数被调用的时候,就会到符号表中去查找对应的函数名,来获取函数的地址,再链接到一起

先看C语言是怎么处理的


通过反汇编我们可以看到,C语言并没有对函数名进行处理,也就是说无论我们参数的个数,参数的类型,参数的顺序怎么修改,它只认函数名,如果出现了第二个相同函数名的,就算重定义。

下面再看C++的


这里可以看到,C++对函数名进行了处理,函数以_Z4开头,接着是函数名,最后是所有参数的缩写。
_Z是所有函数的前缀,4是函数名的字符个数,例如第一个_Z4testii则代表函数名为test,具有四个字符,参数分别是ii。

这也就是为什么返回值不同和参数名不构成重载的原因,C++正是通过这种函数名修饰规则来实现函数的重载。

extern “C”

有时候我们在使用C++的使用,对于某些函数想让他按照C的风格来编译,就在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。


引用

引用的概念

引用是给某一个对象起了另外一个名字,可以通过这个别名来对原对象进行操作,同时编译器不会对引用变量开辟空间,它和它所引用的对象共用一个空间。

用法:类型 & 引用对象名 = 引用实体

int main()
{int i = 5;int& j = i;cout << i << ' ' << j << endl;j = 8;cout << i << ' ' << j << endl;return 0;
}

引用的特性

  1. 引用在定义的时候必须初始化(因为引用是某个对象的别名,所以必须初始化)
  2. 一个对象可以有多个引用
  3. 一旦引用一个实体,就不能再引用别的实体(有点类似指针的顶层const)

常引用

int main()

{const int i = 5;int& j = i;//错误的const int & k = i;正确的
}

这里对常量i进行引用,因为i是个常量,所以它的值是无法修改的,所以我们使用普通的引用的时候,就会将它的权限放大,导致可以通过j来对i进行修改,这是不合理的,所以编译器会报错。只有使用常量引用才行

int main()
{int x = 6;const int & y = x;
}

同时,我们用常量引用来引用这个x,x是可以修改的,而y无法修改,使权限收缩,变为只读,所以这是可行的。

引用的跨类型

int main()
{double x = 3.14;int& y = x;//错误的const int& z = x;//正确的return 0;
}

这里我们分别使用y和z来引用x,但是这时编译器会提示,y报错,z合法,同样是跨类型,为什么会这样呢?

因为当类型不同的时候会先用一个临时量来引用x,然后再对这个临时量进行引用

int main()
{double x = 3.14;int& y = x;/*等价于const int &temp = x;int &y = temp;*/const int& y = x;/*等价于const int &temp = x;const int &z = temp;*/return 0;
}

如果是普通的引用,这时我们引用的实际上是temp,但是我们是想要对x进行修改的,但是这时不行的,因为临时量具有常性,所以这种行为也是非法的。

如果是常量的引用,表明我们不会对x进行修改,所以就算引用的其实是临时量temp,这个行为也是合法的

引用的使用场景

  1. 作为参数
struct A
{int arr[1000000];
};void test(A& s1)
{}

假设我们存在一个超级大的结构体,如果我们直接将结构体传过去的话,会产生一个临时变量来将这个结构体拷贝到形参中,这是极大的开销,但如果我们使用引用的话,传的只是一个别名而已,但是需要注意的和上面一样,因为临时量具有常性,所以如果我们要传递一个常量,就必须要在引用前加上const。

  1. 作为返回值
> int& Add(int a, int b)
{int c = a + b;return c;
}int main()
{int& ret = Add(1, 2);Add(3, 4);cout << ret << endl;return 0;
}

对于这样一个代码,我们可能第一眼觉得ret会是3

但是其实是7.

因为我们返回的是c的一个引用,但是c是只存在于调用时的那个栈帧的,调用结束后那个栈帧就会被销毁,虽然销毁后数据不会被清空,但是那片区域的访问权限就会被放开,有可能会被下次调用的函数使用,也有可能会被哪里的一个操作给使用,所以这是一种极为不安全的行为,如果我们要返回一个引用的话,就必须要保证那个对象出了函数的作用域中还会仍然存在。
上面的7是第二次调用后修改了c的值。

所以,如果需要引用作为返回值,就必须保证出了函数作用域,返回的对象没有归还给系统,仍然存在。

以值作为参数或者返回值时,在传参和返回的时候,都会传递或返回原变量的一个临时的拷贝,这样的效率是非常低下的,尤其是数据特别大的时候,但如果使用引用作为参数的话,就不会有这样的问题。

引用和指针

前面我们说了,引用是对象的一个别名,没有独立的空间,和其引用的实体共用一个空间,但是这仅仅是语法概念上的。同时我们发现,引用其实和指针很像,它更像一个顶层const的指针,所以我们可以进入反汇编看看他们之间有没有关系

int main()
{int x = 5;int& y = x;int* z = &x;return 0;
}


反汇编下我们可以看到,指针和引用在汇编下的实现竟然是一模一样的。

所以我们可以得出一个结论,引用是按照指针来实现的,在指针的基础上又给他封装了新的功能

引用和指针的不同点:

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型 实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全

内联函数

inline关键字修饰的函数就是内联函数,在编译时编译器会将函数的代码在调用内联函数的地方展开,减去了函数压栈的开销,提升程序运行的效率

例如这样一个简单的代码
如果我们直接调用它

在汇编下可以看到,他会创建一个新的栈帧,将参数3,4压栈,然后计算完再返回结果
而如果在函数前面加上inline使其变为内联函数

这时再看,就会发现它直接把函数的代码在调用处直接展开,不会再创建新的栈帧。

内联函数的特性

  • 内联函数是一种用空间换时间的做法,省去了创建栈帧和压栈的开销,但也因此代码很复杂和具有循环或递归之类的函数不适合作为内联函数,就算声明为内联函数编译器也会自动将其忽略。
  • 内联函数不能声明和定义分离,因为一旦声明为内联函数,在调用的时候就会直接展开,没有了函数的地址,就无法将其链接到定义的部分。

值得一提的是,内联函数与C语言中的宏函数有些类似,虽然宏的性能不错,但是因为宏缺乏类型的安全检查和无法调试(在预处理阶段就进行了宏替换),在C++中宏函数被内联函数替代宏常量定义被const取代。


auto

在编程时我们常常需要把表达式的值赋给变量,但有时我们又不知道表达式的类型是什么,为了解决这个问题,C++11中引入了auto类型说明符,用它能够让编译器代替我们来分析表达式的所属类型

因为auto需要让编译器通过初始值来推导变量的类型,所以auto必须要有初始值。

int main()
{auto i = 2.7;cout << i <<"的类型为:" << typeid(i).name() << endl;
}

auto的使用规则

  1. auto可以与指针和引用结合
int main()
{int i = 4;auto a1 = &i;auto *a2 = &i;auto& a3 = i;cout <<"a1的类型为:" << typeid(a1).name() << endl;cout << "a2的类型为:" << typeid(a2).name() << endl;cout << "a3的类型为:" << typeid(a3).name() << endl;
}


因为auto可以识别指针类型,所以加不加*都可以,但是引用必须加上&,因为C++没有引用这个类型,引用只是一个修饰别名,所以它的本质还是int。

  1. 在同一行定义多个变量
int main()
{auto i = 1, j = 2; //正确,同一行都是相同类型auto x = 3, y = 4.8;//错误,同一行类型不同
}
  1. auto不能作为函数的参数

    auto不能作为形参的类型,编译器无法对i和j的类型进行推导。

  2. auto不能直接用来声明数组

  3. auto一般会忽略顶层const

int main()
{const int i = 5;//i : const int auto a1 = i;//忽略了顶层const ,al : intconst auto a2 = i;//加上const, a2 : const int
}

范围for循环

对于一些有范围的集合,我们可能不知道他的长度或者在使用循环的时候不小心越界,c++11为了解决这个问题,引入了范围for循环。

循环分为两部分,第一部分是变量的类型,第二部分是被迭代的范围,两者中间用:隔开.

int main()
{vector<int> vec{ 1, 3, 5, 7, 9 };for (auto i : vec){cout << i << ends;}}


需要注意的是,这里的i是对原数据拷贝的一个临时变量,如果要修改原数据的话需要改成&i(既对原数据的引用)


C++ 基础 : 函数重载、引用、内联函数、auto、范围for循环相关推荐

  1. C++ C++基础语法入门总结(二)引用-内联函数-C++11新特性

    C++基础语法入门总结 C++引用 再谈引用和指针 C++内联函数 附加C++11新特性 auto关键字 基于范围的for循环 指针空值nullptr C++引用 引用:就是某一变量(目标)的一个别名 ...

  2. C++学习笔记之——引用 内联函数

     本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 作者 ...

  3. 函数模板、 内联函数

       函数重载就是有相同的函数名但参数的个数或类型不同从而根据不同的参数个数和参数类型来调用相应的方法.    我们发现函数重载只是解决了函数命名的问题,但函数体虽然相同我们还是要重复的写,为了解决这 ...

  4. C++中虚函数可以是内联函数吗?

    1.需要注意的几点: 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联. 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因 ...

  5. Kotlin学习笔记 第三章 函数 高阶函数 lambda表达式 内联函数

    参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...

  6. C++的内联函数和非内联函数的区别

    一.内联函数和非内联函数的定义 1.内联函数 inline void test(); void test() { cout<<"test"<<endl; } ...

  7. [课程视频]指针、引用、函数参数、内联函数等

    今天气温低.风大,我穿一个略正式的T恤衫.一条大短裤和人字拖在办公室里录制最新一期课程视频.反正拍不到下半身,所以下半身衣着随意我面向的录制环境如下图,我背后是一个绿幕.设备由广东省教学改革项目资金提 ...

  8. inline 内联函数详解 内联函数与宏定义的区别

    一.在C&C++中 一.inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义. 表达式形式的宏定义一例: #define ExpressionName ...

  9. 【C++入门】函数重载,引用,内联函数......

    目录 命名空间 C++的输入与输出 缺省参数 函数重载 引用 内联函数 auto关键字 命名空间 在C++的学习过程中,比起C语言,C++有着更多的函数,关键字等等,但是他们都存在于全局作用域中,就可 ...

最新文章

  1. 一篇非常好的transformer年度总结
  2. 新的地方门户社区论坛前期运营思路及指南
  3. 1.Rabbitmq学习记录《本质介绍,协议AMQP分析》
  4. 服务器系统日志有哪些centos,CentOS 分析服务器日志命令
  5. Linux命令----用户管理
  6. 详解Python的内置函数eval()
  7. NOKOV Seeker2.2动作捕捉软件与ROS的通信
  8. 计算机的音量找不到了,音量控制不见了_电脑右下角调音量的小喇叭没有了,是怎么回事?该......
  9. IT十大风云人物(转水冰洋)
  10. Dreamweaver——滚动字幕制作方法总结
  11. Android 图片与屏幕坐标点
  12. 谷歌浏览器chrome翻译插件完美解决开发者模式插件问题
  13. 清华计算机系出了哪些牛人,清华大学16位学霸PK 简历吓坏网友
  14. 中国不是不能开发出自己的浏览器,而是没必要
  15. 手机屏幕到底要多大才算是个头?
  16. 二叉树算法--数据结构课程设计
  17. linux上运行unity3d,ubuntu系统怎么玩unity3d游戏?
  18. iOS开发之SDK开发
  19. seo点击软件,seo点击软件网站
  20. 后台管理框架 Vue Admin

热门文章

  1. 使用Java读取 “Python写入redis” 的数据踩坑记录
  2. 什么是设计模式(Design Patterns)
  3. Cortex-M3-建立堆栈
  4. Java学习需要掌握哪些技能?
  5. 在js中如何判断一个对象是否为空
  6. Linux打tar包排除目录中的某个目录
  7. centos操作系统搭建Lamp环境(apache php mysql)
  8. sas Data步数据读取流程详解
  9. 使用reserve函数避免vector和string的内存重新分配
  10. MySQL查询的性能优化