C++进阶—第五章 函数

5.函数

5.1函数基本知识

函数由返回值、函数名、形参列表、函数体构成。由于C++的编程风格,将main()函数放在最前面,因此若main()中对某个函数进行调用,需要在main()前面进行函数声明,而函数的定义则放在main()的后面。

int fun_test(int m);   //函数声明,又称函数原型

int main(void)

{

...

fun_test(2);

return 0;

}

 

int fun_test(int m)    //函数定义

{ //函数体

return 2*2;

}

1.返回值

对于有返回值的函数,其返回值的数据类型写在函数名的前面,如int fun_test(int m)在函数体中用return语句将数据返回给调用函数(main函数是调用函数,int fun_test(int m)是被调用函数)。注:返回值不能是数组,其他数据类型都可以。

对于没有返回值的函数,其返回值数据类型使用void;函数体中可以不使用return语句,也可以使用return语句。

#include<iostream>

using namespace std;

void max(int &m, int &n);

 

int main(void)

{

int j = 3, k = 3;

max(j, k);

int a = 11, b = 14;

max(a,b);

cout << a <<endl<<b<< endl;

return 0;

}

 

void max(int &m,int &n)

{

if (m == n)

{

cout << "相等" << endl;

return;  //使用return提前结束函数

}

if(m > n)

n = m;

else

m = n;

}

2.函数名

若函数名相同,则需要进行函数重载,后面再说。

3.形参列表

若没有形参,则采用int fun()int fun(void)这样形式的函数,形参可以省略或使用void。若形参个数不定,可以用int fun(...)的形式,其中需要用到#include<stdarg.h>文件下的一些函数。

#include<iostream>

#include<stdarg.h>

using namespace std;

int add(int count, ...);

 

int main(void)

{

cout << add(3, 2, 3, 4) << endl;

return 0;

}

 

int add(int count,...)

{

int sum = 0;

va_list ap;  //定义一个char *指针,typedef char* va_list

va_start(ap, count);  //对ap初始化,指向形参列表中的第一个参数m

for (int i = 0; i < count; ++i)

{

sum += va_arg(ap, int);  //va_arg(ap, int)返回ap下一个指向int型数据的数值;同时ap地址加一

}

va_end(ap);  //释放指针

return sum;

}

(1)形参和数组

int sum_arr(int arr[], int n);  //arr实际是数组首元素的指针,n表示数组的长度

int sum_arr(int * arr, int n);

因为数组做形参实际上传递的是数组首元素的指针,因此其操作的是实参,若在被调用函数中不改变数组,则需要加const。

int sum_arr(const int arr[], int n);   //函数中不能修改arr数组的数值

int sum_arr(const int * arr, int n);

二维数组:

int sum_arr2(int (*arr2)[4], int n);  //表示二维数组arr2,行数为n,列数为4

int sum_arr2(int arr2[][4], int n);  //表示二维数组arr2,行数为n,列数为4

(2)形参和C-风格字符串

int par_c(char arr[]);   //C-风格字符串是用字符数组来表示的

int par_c(char * arr);  //C-风格字符串的本质是char *

由于传递的也是指针,因此可以用const表示字符串只读。

int par_c(const char arr[]);

int par_c(const char * arr);

(3)形参和类/结构体

#include<iostream>

using namespace std;

class Person

{

public:

int age;

const char * name;

void print_info()

{

cout << name << "'s age is " << age << endl;

}

};

void modify_age(Person& per);

int main(void)

{

Person per1;

per1.age = 18;

per1.name = "zhangsan";

modify_age(per1);

per1.print_info();  //打印zhangsan's age is 20

return 0;

}

 

void modify_age(Person& per)

{

per.age = 20;

}

modify_age传递的参数不适用引用,则会打印zhangsan's age is 18。因此若要修改类对象中的数据成员,则形参中需要使用引用。否则,不会修改实参中对象的数据成员。

(4)形参和string对象

string的本质也是类,因此若要修改实参中的string对象,则形参需要加引用。

#include<iostream>

#include<string>

using namespace std;

void modify_str(string &str);

 

int main(void)

{

string str = "this is a test";

cout << str << endl;

modify_str(str);

cout << str << endl;

return 0;

}

 

void modify_str(string &str)

{

str += "!";

}

运行结果:

this is a test

this is a test!

5.2内联函数

当进行常规的函数调用时,程序将函数调用指令的地址保存在寄存器lr(这里以ARM寄存器为例)中,并将函数的参数复制到栈中;跳到被调用函数的地址开始执行,执行完后将返回值放入寄存器中;最后读取lr中值,并跳到此地址处,开始执行下一条指令。这样来回跳跃给cpu带来了一定的开销。

C++中提供了内联函数,其本质是编译器将相应的函数代码替换了原来的函数调用指令。这样无需跳转,函数执行的速度就会快很多;但是内存是有限的,因此内联函数只能用于那些比较简短的函数中,比如3-4行的函数。

#include<iostream>

#include<string>

using namespace std;

inline void modify_str(string &str);

 

int main(void)

{

string str = "this is a test";

cout << str << endl;

modify_str(str);

cout << str << endl;

return 0;

}

 

inline void modify_str(string &str)

{

str += "!";

}

内联函数:在函数声明前加关键字inline;在函数定义前加关键字inline。通常会省略函数声明,而把函数定义放在函数声明的地方。

#include<iostream>

#include<string>

using namespace std;

inline void modify_str(string &str)

{

str += "!";

}

 

int main(void)

{

string str = "this is a test";

cout << str << endl;

modify_str(str);

cout << str << endl;

return 0;

}

5.3函数重载

函数重载是当函数名相同时,通过判断形参列表的不同来进行选择调用。对于编译器来说,当两个函数名相同时,其形参的数量不同、数据类型不同、不同类型参数的顺序不同都需要进行函数重载。

函数重载的本质是编译器对形参列表进行判断,从而判定这2个函数本质上是不同的。其实编译器会对函数名进行一定规律的改变,这也导致此编译器编译出的代码不能在另一个编译器上运行,因为每个编译器对函数名的处理算法是不同的。

#include<iostream>

using namespace std;

int fun_add(int a, int b);

double fun_add(int a, double b);

double fun_add(double a, int b);

double fun_add(double a, int b, int c);

 

int main(void)

{

cout<<fun_add(1, 2)<<endl;

cout<<fun_add(1, 2.1)<<endl;

cout<<fun_add(2.1, 1)<<endl;

cout<<fun_add(2.1,1,2)<<endl;

return 0;

}

 

int fun_add(int a, int b)

{

cout << "int add(int a, int b)" << endl;

return a + b;

}

double fun_add(int a, double b)   //参数类型不同

{

cout << "double fun_add(int a, double b)" << endl;

return a + b;

}

double fun_add(double a, int b) //参数顺序不同

{

cout << "double fun_add(double a, int b)" << endl;

return a + b;

}

double fun_add(double a, int b,int c)  //参数数量不同

{

cout << "double fun_add(double a, int b,int c)" << endl;

return a + b + c;

}

运行结果:

int add(int a, int b)

3

double fun_add(int a, double b)

3.1

double fun_add(double a, int b)

3.1

double fun_add(double a, int b,int c)

5.1

C++中函数重载中,返回值类型的不同是无法判断函数不同,因为函数都是先执行,再返回。

5.4函数模板

1.函数模板的基本知识

使用泛型编程的思想来定义函数,其实现是函数模板。

#include<iostream>

using namespace std;

template<class T>  //可以使用template<typename T>

T fun_add(T a, T b);

 

 

int main(void)

{

cout<<fun_add(1, 2)<<endl;

cout<<fun_add(2.1,2.3)<<endl;

return 0;

}

 

template<class T>  

T fun_add(T a, T b)

{

T temp = 10;

cout << "T fun_add(T a, T b)" << endl;

return a + b + temp;

}

函数模板并不是函数,而是当main()执行fun_add(1, 2)时,编译器对函数模板实例化,生成一个int fun_add(int a, int b)函数,此时T = int,即将模板中所有的T替换成int。

函数模板的声明和定义都需要使用template<class T> 告诉编译器这是一个模板。C++98之后添加了关键字typename,因此也可以使用template<typename T>其中的T表示通用的数据类型,其可以是基础数据类型、指针、结构体、类,但不能是引用、数组。引用需要template<typename T>  T fun_add(T &a, T &b)这样表示;数组需要template<typename T>  T fun_add(T arr[], int n)这样表示。

2.函数模板、模板具体化和常规函数的选择

#include<iostream>

using namespace std;

template<typename T>  

T fun_add(T a, T b);

double fun_add(double a, double b);

 

int main(void)

{

cout<<fun_add(1, 2)<<endl;

cout<<fun_add(2.1,2.3)<<endl;

return 0;

}

 

template<typename T>

T fun_add(T a, T b)

{

cout << "T fun_add(T a, T b)" << endl;

return a + b;

}

 

double fun_add(double a, double b)

{

cout << "double fun_add(double a, double b)" << endl;

return a + b;

}

运行结果:

T fun_add(T a, T b)

3

double fun_add(double a, double b)

4.4

当函数调用有2种选择时,即常规函数和函数模板,优先调用常规函数。

#include<iostream>

using namespace std;

template<typename T>  

T fun_add(T a, T b);

template <> double fun_add<double>(double a, double b);

 

int main(void)

{

cout<<fun_add(1, 2)<<endl;

cout<<fun_add(2.1,2.3)<<endl;

return 0;

}

 

template<typename T>

T fun_add(T a, T b)

{

cout << "T fun_add(T a, T b)" << endl;

return a + b;

}

 

template <>

double fun_add<double>(double a, double b)

{

cout << "template <> double fun_add<double>(double a, double b)" << endl;

return a + b;

}

当函数调用有2种选择时,即函数模板和模板具体化,优先调用模板具体化。

3.关键字decltype

template<typename T1, typename T2>

type? fun_add(T1 a, T2 b)

{

type? = a + b;

}

当使用T1和T2来作为数据类型时,不知道a+b的数据类型,也不知道返回值的数据类型时,该怎么办呢?C++11提供了关键字decltype,用它来声明或初始化一个变量,其数据类型和括号内的数据类型相同。

#include<iostream>

using namespace std;

template<typename T1, typename T2>

auto fun_add(T1 a, T2 b) ->decltype (a + b);

 

int main(void)

{

cout<<fun_add(1.1, 2)<<endl;

cout<<fun_add(2,2.3)<<endl;

return 0;

}

 

template<typename T1, typename T2>

auto fun_add(T1 a, T2 b) ->decltype (a + b)  //使用auto和decltype来声明返回值的类型和a+b相同

{

cout << "auto fun_add(T1 a, T2 b) ->decltype (a + b)" << endl;

decltype (a + b) temp; //定义一个变量temp,其数据类型和a+b相同

temp = a + b;  //或者直接decltype (a + b) temp = a + b;

return temp;

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值