Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++函数论

C++函数论

作者头像
Cloudox
发布于 2022-01-07 12:54:24
发布于 2022-01-07 12:54:24
34700
代码可运行
举报
文章被收录于专栏:月亮与二进制月亮与二进制
运行总次数:0
代码可运行

关于C++的函数有很多知识,因为其函数有多种变体,可以说C++创作者为了开发方便,打开了很多个后门让编程人员随心所欲地炫技使用,但私以为这也造成了使用函数时的复杂度,如果真的在代码中使用各种变体,虽然确实可以让代码看上去简洁高级,但是对于代码阅读来说却并不是特别友好。

不过,无论如何,C++提供了很多可能,这里也稍微总结一下其函数的各种变体。

定义常规函数

定义函数最简单也最常见,就是定义最常规的函数,有返回值类型、函数名、参数类型、参数名和代码块:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void swap (int a, int b) {
  int temp = a;
  a = b;
  b = temp;
}

这种函数定义只要是程序员就会熟悉了,不多说。

函数原型

什么是函数原型?为什么要用原型?

看过c/c++代码的人都知道,其代码大都有个main函数,而且一般都放在最前面,而其余自定义的函数都放在后面,这就导致如果你要在main函数中调用一个自定义函数,此时因为你的自定义函数是在后面定义的,编译器不懂你的调用语句,因此我们需要在main函数之前声明一个函数原型,表明函数的返回值类型、函数名、参数类型、参数名,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void swap (int a, int b);

int main (void) {
  int a = 1;
  int b = 2;
  swap (a, b);
  return 0;
}

void swap (int a, int b) {
  int temp = a;
  a = b;
  b = temp;
}

值得一提的是,函数原型中的参数名其实可以不用写,因为只有参数类型和数量才是函数的标志,毕竟都只是个带好罢了。

参数传递方式

参数传递方式是什么意思?这里我们指的是传递的到底是变量的拷贝还是变量本身。

如上述swap函数那样的,其实是在调用函数时拷贝了一份变量,因此在函数内随意改变参数变量值,原本的变量都不会变的。

但是我们也可以直接传递原本的变量本身,这可以通过传递指针或者变量引用来做到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 指针
void swap (int * a, int * b);

// 引用
void swap (int & a, int & b);

指针会传递变量的地址,引用故名思议也就是传递原变量的一个引用,因此都是指向原变量的,因此这样在函数内修改变量值的话,原变量是会跟着改变的,如果不想改变,可以用const来修饰参数。

函数指针

其实和变量一样,函数也是可以有指针的,创建方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int pam (int);
int (*pf) (int);
pf = pam;// pf 就指向了函数pam,同样的用*pf来调用函数

这个知识点其实用的并不多。

内联函数

内联函数并不是一种函数变体,而是指编译器的处理方式不同。

普通的函数调用是遇到函数调用时,将跳到函数代码的地址去,执行后返回原地址,而内联函数在编译时会直接编译到调用的代码中去,因此无需做跳转,因此内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。显然,内联函数不能递归,否则就是无限地内联去插入代码了。

要定义为内联函数,直接省略原型,将整个定义放上去,并在函数定义前加上关键字inline:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inline double square (double x) { return x * x; }

// 在C语言下使用宏来实现:
#define SQUARE (X) X*X

默认参数

和python一样,c++允许给函数的参数设置默认值,如果在调用时没有给对应参数赋值,那么函数将使用默认值,方法其实就是在声明函数原型时同时声明参数的默认值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void add (int a, int b = 5);

int main() {
  int a = 1;
  add(a);
  return 0;
}

要注意的是有默认值的参数必须全部在右边,这也是为了调用时方便参数的设置,因为在调用时你要写参数,不可能参数空一个不写,而写完了你要设置的,剩下的就都是右边默认的了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int func1 (int n, int m = 4, int j = 5); // 有效
int func2 (int n, int m = 6, int j); // 无效

// 不允许这样调用:
func(1, , 2);// 无效

函数重载

c++允许有多个同名函数,只要其参数的类型或者数量不一样就可以了。

比如你可以有同样的名为swap的函数,有的参数是int类型,有的参数是double类型,有的有三个参数,等等,这些c++会认为是不同的函数,在你调用时视你传递的参数类型会自动调用对应函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void swap (int a, int b);
void swap (double a, double b);
void swap (int a, int b, int n = 5);

但是只有参数的类型和数量才是函数的特征标,仅仅返回类型不同的同名函数是不行的,必须在参数上有不同。

这还有一个名字,叫多态,有没有熟悉一点了。

函数模板

为了炫技,c++又定了函数模板这种幺蛾子,它定义一种通用函数,函数的实现方法一致,但是不限死参数类型,也就是说同一个函数,你传的参数可以是int型,也可以是double型。注意,这和上面的函数重载是有区别的,实际上也是进一步节省了函数重载的工作,不用定义那么多相同名字参数不同的函数,写那么多代码,只写一个就行了,参数类型根据传递进来的而定,即参数可配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 函数原型
template <class Any> // or typename Any
void Swap (Any &a, Any &b);

// main函数

// 函数定义:
template <class Any> // or typename Any
void Swap (Any &a, Any &b) {
  Any temp;
  temp = a;
  a = b;
  b = temp;
}

注意这里参数类型用Any代替了,也就是说你既可以传int也可以传double等等,随你传,反正结果就是交换两个变量的值。

函数模板也是可以重载的,这里既然参数的类型是不定的,但是你可以改变参数数量呀,而且也不要求所有参数类型都是布丁,可以有部分是定死的,这样就可以玩出无数花样来了,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
template <class Any> // or typename Any
void Swap (Any &a, Any &b);// 注意这里是传递的变量引用了

template <class Any> // or typename Any
void Swap (Any &a, Any &b, int c);

真是套路多。

显示具体化

上面的函数模板让所有的参数类型使用同一个函数代码块执行同一种操作,如果我这时候又想对某个特定类型做不同的操作怎么办?就你名堂多。

一种方法是使用函数重载。

另一种方式就是显示具体化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct job {
  // 假设有一个job结构体
}

// 普通的函数模板
template <class Any> // or typename Any
void Swap (Any &a, Any &b);

// job特供版
template <> void Swap<job> (job &, job &);

然后在job特供版的函数你自己就可以去玩出花了。

编译器到底用哪个函数版本?

上面说了这么多种函数,可能在函数原型部分我有一大堆同名但是各个细节部分不同的函数原型,当在代码中调用的时候到底编译器怎么判断用哪个呢?换言之也就是这些调用是怎么排序的呢?

从最佳匹配到最差匹配顺序如下:

  1. 完全匹配,但常规函数优先于模板。
  2. 提升转换(需要转换参数类型,如char和shorts自动转换为int,float自动转换为double)。
  3. 标准转换(需要转换参数类型,如int转换为char,long转换为double)。
  4. 用户定义的转换,如类声明中定义的转换。

目前学习到了这些函数变体,如有其它,继续补充。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017.12.01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++初阶:入门泛型编程(函数模板和类模板)
现在就轮到泛型编程出场了:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
是Nero哦
2024/02/01
2660
C++初阶:入门泛型编程(函数模板和类模板)
C++从入门到精通——模板
C++模板是C++语言中的一种泛型编程技术,可以实现在编译期间生成不同类型的函数或类。通过使用模板,可以编写通用的代码,使其能够处理多种不同类型的数据。
鲜于言悠
2024/04/24
1490
C++从入门到精通——模板
【C++修炼之路】7. 模板初阶
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。
每天都要进步呀
2023/03/28
3710
【C++修炼之路】7. 模板初阶
c++之函数探幽笔记
概念:内联函数是c++为提高程序运行速度的一项改进。内联函数编译器将使用相应的函数代码替换函数调用。
艰默
2022/11/22
3840
c++之函数探幽笔记
C++函数模板入门教程
在《C++函数重载》一节中,为了交换不同类型的变量的值,我们通过函数重载定义了四个名字相同、参数列表不同的函数,如下所示:
C语言与CPP编程
2020/12/02
3110
C++之函数模板(一)
C++是一门非常强大,非常复杂,非常庞大的语言。C++本身从C语言发展而来,它一开始就具备了面向过程编程和面向对象编程这两种编程范式(编程风格,其实就是程序设计的不同哲学理念)。后来随着C++的逐步发展,它支持了泛型编程。本文将要介绍的函数模板就是泛型编程的程序设计理念。再后来C++有了模板元编程,这本质上就是函数式编程。在C++11标准中又引进了lambda表达式。到此为止,C++已经变得非常复杂,非常庞大,同时也非常强大。我不太了解函数式编程的设计理念是什么。从百度百科的介绍来看,应该是函数作为程序设计的第一位。C++本身并不提倡使用函数式编程风格。
zy010101
2020/04/08
4220
C++中的标准化工厂—— 模板
        众所周知,C++是基于C语言的编写,所以它也继承了众多C的特性(当然也包括部分缺点),且基于它们进行改良和优化,这篇文章要讲的是模板,这算的上是C++基于C的一个“懒人利器”
比特大冒险
2023/04/16
8070
C++中的标准化工厂—— 模板
【C++】模板初阶:泛型编程的起点
泛型编程是一种编程范式,它允许在编写代码时使用一种通用的数据类型或算法,以便在不同的数据类型上进行操作,而不需要为每种数据类型编写专门的代码。泛型编程的目标是提高代码的重用性和可扩展性。
大耳朵土土垚
2024/05/06
1760
【C++】模板初阶:泛型编程的起点
C++ 初识函数模板
如果现在有一个需求,要求编写一个求 2 个数字中最小数字的函数,这 2 个数字可以是 int类型,可以是 float 类型,可以是所有可以进行比较的数据类型……
一枚大果壳
2022/09/16
6460
【C++】模板初阶
以实现交换函数为例,在C语言中即使是近乎完全一致的的功能,通过代码实现,只要参数不同,我们就需要写对应类型的不同函数名的函数,在之前的学习中,我们已经学习了函数重载,我们不再需要起不同的函数名,比起C语言方便不少,但是不容忽视的是函数重载仍然有不好的地方。
ZLRRLZ
2024/12/13
1010
【C++】模板初阶
C/C++开发基础——函数模板
模板编程分两种,分别是算法抽象的模板、数据抽象的模板。算法抽象的模板以函数模板为主,数据抽象的模板以类模板为主。
Coder-ZZ
2023/09/04
1750
C/C++开发基础——函数模板
【C++初阶】函数模板与类模板
于是,大佬心里就在想,能不能像铸铁一样,刻出一个模子(模板),然后通过浇筑不同的材料(不同的类型),从而锻造成不同材料制成的宝刀(不同类型的目标代码)
MicroFrank
2023/01/16
8220
【C++】初识模板
在谈及本章之前,我们先来聊一聊别的。橡皮泥大家小时候应该都玩过吧,通常我们买来的橡皮泥里面都会带有一些小动物的图案的模子。我们把橡皮泥往上面按压,就会得到一个个具有该图案形状的橡皮泥。橡皮泥的颜色不同,得到的形状的颜色也不相同。就好像下面这样:
诺诺的包包
2023/03/24
5670
【C++】初识模板
C++之模板(上)
本文介绍了C++模板的基础概念,简单介绍了泛型编程,模板,以及模板中的函数模板与类模板等相关概念。
摘星
2023/04/28
4330
C++之模板(上)
【C++】C++模板基础知识篇
所以c++就提供了模板,就相当于一个模具,让编译器根据不同的类型利用该模子来生成代码。
zxctscl
2024/03/09
1280
【C++】C++模板基础知识篇
[C++] 模版初阶
如上列代码,靠函数重载进行实现多个不同数据类型的变量完成交换,过于繁杂且代码量大,所以在这种时候需要使用模版来解决。
DevKevin
2024/07/25
2020
[C++] 模版初阶
【C++】泛型编程——模板初阶
C++引入了泛型编程,就可以解决这个问题。 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。 模板是泛型编程的基础,又分为函数模板和类模板。
YIN_尹
2024/01/23
1830
【C++】泛型编程——模板初阶
C++初阶-模板初阶
C++模板初阶 零、前言 一、泛型编程 二、函数模板 1、函数模板定义及使用 2、函数模板原理 3、函数模板实例化 4、函数模板匹配原则 三、类模板 1、类模板定义及使用 2、类模板实例化 零、前言 本章主要讲解C++的模板相关的初阶知识 一、泛型编程 用函数重载来实现交换变量函数: void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, d
用户9645905
2022/11/30
4580
C++初阶-模板初阶
[C++]模版初阶
当我们想用一个函数完成多个类型参数的操作时,发现每次都要重新再写一个函数再使用,对于重载的函数虽然可以使用,但是每次用新的类型都需要再去重载一次函数**。** 例如实现交换的函数:
DevKevin
2024/05/24
2550
[C++]模版初阶
C++打怪升级(八)- 泛型编程初见
在C++中我们学习了函数重载,可以写多个同名参数类型不同的函数来实现; C++函数重载解决了函数同名的问题,但是我们还是要写多个函数,而它们仅仅只有类型不同;
怠惰的未禾
2023/04/27
8510
C++打怪升级(八)- 泛型编程初见
相关推荐
C++初阶:入门泛型编程(函数模板和类模板)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验