第三章 函数和函数模板
**大家想一起学习交流的可以加群,QQ:755422568。**
一、函数的参数及其传递方式
C++的函数传递有两种传递方式:传值和传引用。传值分为传“对象值”和“对象地址值”,“对象值”是指对象的数据成员值,“对象地址值”是指对象的首地址值。伟引用其实是传对象的地址,称为传地址方式。
注意:传地址值传递的是值,不是地址
传地址传的是地址,不是地址值。
传递对象地址值是使用对象指针作为参数
传递对象地址是使用对象引用作为参数。
(1)、对象作为函数参数
将对象作为函数参数,是将实参对象的值传递给形参对象,单向传递。形参拥有实参的备份,当函数中改变形参值时,改变的只是备份中的值,不会影响原来实参的值。
优点:可以防止被调用的函数改变参数的原始值。
#include <iostream>
#include <string>
using namespace std;
void swap(string,string); //使用string类的对象作为函数参数
void swap(string s1,string s2){ //string类的对象s1和s2作为函数参数
string temp = s1;s1 = s2;s2 = temp;
cout << "交换为:str1 = " <<s1 << "str2 = " << s2 << endl;
}
void main(){
string str1("现在"),str2("过去"); //定义对象str1和str2
cout << "交换前:str1 = " <<str1 << "str2 = " << str2 << endl;
swap(str1,str2); //使用传值方式传递str1和str2的数据成员值
cout << "返回后:str1 = " <<str1 << "str2 = " << str2 << endl;
/**
*运行结果:
*交换前:str1 = 现在 str2 = 过去
*交换为:str1 = 过去 str2 = 现在
*返回后:str1 = 现在 str2 = 过去
*/
}
(2)、对象指针作为函数参数
将指向对象的指针作为函数参数,形参是对象指针,实参是对象的地址值
。虽然参数传递方式仍然是传值,但因为形参传递的就是实参本身,所以当在函数中改变形参的值时,改变的就是原来实参的值。传对象地址值要用到对象指针,常使用的传对象地址值的方式,如传递类或结构对象的地址值、传递数组名。
#include <iostream>
#include <string>
using namespace std;
void swap(string * ,string *); //使用string类的指针作为函数参数
void swap(string * s1,string * s2){
string temp = * s1;* s1 = s2; * s2 = temp;
cout << "交换为:str1 = " <<s1 << "str2 = " << s2 << endl;
}
void main(){
string str1("现在"),str2("过去"); //定义对象str1和str2
cout << "交换前:str1 = " <<str1 << "str2 = " << str2 << endl;
swap(&str1.&str2); //使用传地址值方式传递str1和str2的地址值
cout << "返回后:str1 = " <<str1 << "str2 = " << str2 << endl;
/**
* 运行结果:
*交换前:str1 = 现在 str2 = 过去
*交换为:str1 = 过去 str2 = 现在
*返回后:str1 = 过去 str2 = 现在
*/
}
传递数组名实例
#include <iostream>
using namespace std;
void swap(int []);
void swap(int a []){
int temp = a[0];a[0] = a[1]; * a[1] = temp;
cout << "交换为:a = " << a[0] << "b = " << a[1] << endl;
}
void main(){
int a[] ={3,8}; //定义数组a
cout << "交换前:a = " << a[0] << "b = " << a[1] << endl;
swap(a); //传递数组名a(指针名)
cout << "返回后:a = " << a[0] << "b = " << a[1] << endl;
/**
* 运行结果:
* 交换前:a = 3 b = 8
* 交换为:a = 8 b = 3
* 返回后:a = 8 b = 3
*/
}
(3)、引用作为函数参数
在函数调用时,实参对象名传给形参对象名,形参对象名就成为实参对象名的别名,实参对象和形参对象代表同一个对象,所以改变形参对象的值就是改变实参对象的值。
实际上,在传递地址时,是把实参对象的地址传给形参对象,使用形参对象的地址取实参对象的地址,从而使用形参对象和实参对象共享同一个单元,这就是地址传递方式。
#include <iostream>
#include <string>
using namespace std;
vodi swap(string&,string$); //使用string类的引用对象作为参数
void swap(string &s1,string &s2){
string temp = s1;s1 = s2; s2 = temp;
cout << "交换为:str1 = " <<s1 << "str2 = " << s2 << endl;
}
void main(){
string str1("现在"),str2("过去"); //定义对象str1和str2
cout << "交换前:str1 = " <<str1 << "str2 = " << str2 << endl;
swap(str1.str2); //使用传地址值方式传递str1和str2的地址值
cout << "返回后:str1 = " <<str1 << "str2 = " << str2 << endl;
/**
* 运行结果:
*交换前:str1 = 现在 str2 = 过去
*交换为:str1 = 过去 str2 = 现在
*返回后:str1 = 过去 str2 = 现在
*/
}
为什么传引用比传指针要更好?
(简答题)
系统向形参传递的是实参的地址而不是实参的值,通过引用参数,一个函数可以修改另一个函数内的变量。因为引用对象不是一个独立的对象,不单独占用内存单元,而对象指针要另外开辟内存单元(其内容是地址)。
(3)、默认参数
默认参数就是不要求程序设定该参数,而由编译器在需要时给该参数赋默认值,当程序员需要传递特殊值时,必须显式的指明。默认参数是在函数原型中说明的 ,默认参数可以多于一个,但必须放在参数序列的后面。
#include <iostream>
#include <string>
using namespace std;
void Display(string s1.string s2 = "",string s3 = "");
void Display(string s1,string s2,string s3){
if(s2 == ""&&s3="")
cout << s1 << endl;
else if (s3 =="" && s2 != "" )
cout << s1 << "," << s2 << endl;
else
cout << s1 << "," << s2 << "," << s3 << endl;
}
void main(){
string str1("现在"), str2("过去"), str3("将来");
Display(str1);
Display(str1,str2,str3);
Display(str3,str1);
Display(str2,str3);
/**
* 运行结果:
* 现在
* 现在 过去 将来
* 将来 现在
* 过去 将来
*/
}
(3)、使用const保护数据
用const修饰传递参数,是通知函数,它只能使用参数而无权修改它。
扫描二维码关注公众号,回复: 9638567 查看本文章
#include <iostream>
#include <string>
using namespace std;
void change(const string&);
void change(const string&){
string s2 = s + "No!";
cout << s2 << endl;
}
void main(){
string str("Can you change it?");
change(str);
cout << str << endl;
/**
* 运行结果:
* Can you change it? No!
* Can you change it?
*/
}
二、讨论函数返回值
(1)、返回引用的函数
将函数说明为返回一个引用的主要目的是为了将函数用在赋值运算符的左边。表示方法如下:
数据类型& 函数名 (参数列表);
#include <iostream>
using namespace std;
int a[] = {2,4,6,8,10,12};
int& index(int x); //返回引用的函数原型声明
int& index(int x){
return a[i]; //返回指定下标的整数数组内容
}
void main(){
cout << "修改之前:" <<a[3] << endl;
index(3) = 16;
cout << "修改之后:" << index(3) << endl;
}
(2)、返回指针的函数
函数的返回值可以是存储某种类型数据的内存地址,称这种函数为指针函数。表示方法如下:
类型标识符 * 函数名 (参数列表);
#include <iostream>
using namespace std;
float * input(int &);
void main(){
int num;
float * data;
data = input(num); //调用函数,返回指针赋值给data
for(int i=0;i<num;i++){
cout << "值"<< (i+1)<< "是:" << data[i] << endl;
}
delete data; //释放内存空间
/**
* 运行结果:
* 你要输入的数值个数是:3个
* 值1是:10.3
* 值2是:12.5
* 值3是:16.7
*/
}
float * input(int& n){
cout << "你要输入的数值个数是:" << endl;
cin >> n;
if(num <= 0){
return NULL;
}
float * buf = new float[n]; //动态申请分配内存
if(buf == 0){
return NULL;
}
for(int i=0;i<num;i++){
cin >> buf[i];
}
return buf; //返回指针
}
指针所指向的对象的生存期不应低于该指针的生存期。
(3)、返回对象的函数
void example();
string back(string);
int main(){
example();
return 0;
}
void example(){
string s = "zhangsan";
string ss = back(s);
cout << ss;
//zhangsanfeng
}
string back(string s1){
string s2 = s1 + "feng";
return s2;
}
(4)、函数返回值作为函数的参数
#include <iostream>
using namespace std;
int max(int. int);
void main(){
cout << max(55,max(25,39)) << endl;
}
int max(int m1,int m2){
return (m1 > m2)?m1:m2;
}
三、内联函数
① 使用 关键字inline
可以让C++编译器在遇到对函数isnumber调用的时候,都用这个函数体替换调用表达式;
② 由于编译器必须知道内联函数的函数体,才能进行内联替换,因此内联函数必须在程序中第一次调用此函数的语句出现之前定义;
③ 如果程序多次调用某个函数,将会大大降低使用效率,使用内联函数能加快程序执行速度;
④ 具有循环语句、switch语句的函数不能说明为内联函数。
例子:
inline int isnumber(char c){
return (c >= '0' && c <= '9' )?1:0;
}
四、函数重载和默认参数
函数重载可以使一个函数名具有多种功能,即多种形态,称之为多态性。
函数重载的区别:一是参数类型不同,二是参数个数不同。
五、函数模板
(1)、引入函数模板
Type max(Type m1,Type m2){
return (m1 > m2)?m1:m2;
}
Type并不是一种实际的数据类型,在函数实例化时,编译器能用实际的类型来代替它,就好像按照模板来制造新的函数一样,称这种函数为函数模板。与某个具体数据类型连用的过程,称函数模板实例化,其形式是类型参数化。一般用T作为标识符,模板以template开头。
template <class T>
T max(T m1,T m2){
return (m1 > m2)?m1:m2;
}
(2)、函数模板的参数
调用方式:函数模板名 (参数列表)
(3)、使用显示规则和关键字typename
关键字typename用途之一替代template参数列表中的关键字class。
#include <iostream>
using namespace std;
template <typename T>
T max(T m1,T m2){ //最大值
return (m1 > m2)?m1:m2;
}
template <typename T> //必须重写
T min(T m1,T m2){ //最小值
return (m1 < m2)?m1:m2;
}
void main(){
cout << max(1,2) << "," << min(4,3) << endl;
/**
* 运行结果:
* 2 3
*/
}