现在的位置: 首页 > 综合 > 正文

C++基础概念-函数

2018年08月15日 ⁄ 综合 ⁄ 共 19037字 ⁄ 字号 评论关闭

7.1  基本概念

函数可以看成是用户为了解决某特定问题而定义的操作。最常见的函数就是前面章节中所有C++程序实例的main()数。作为C++程序第一个调用的函数,main()函数体通过调用其它函数,共同完成程序需要处理的任务。

7.1.1  函数声明

C++程序中定义使用函数前,必须先声明该函数,否则会引起编译期错误。当然,main()函数之所以使用前没有作声明,是因为函数的定义也可以被当作声明。但是它只能在程序中定义一次。基本的函数声明语法格式如下。

返回类型函数名(参数列表);

同样,函数的声明也是一条以分号结尾的语句。它主要由三个部分组成,分别是函数的返回值类型、函数名称以及对应的参数列表。函数声明也称为函数原型。它的出现可以让函数定义在文件的任意位置。C++中函数的定义使用之前进行原型声明,至少有两个好处。

q  第一,函数中参数因为有了原型声明,编译器针对函数的调用时会对实参类型作检查。

q  第二,函数声明可以放在头文件中,这些头文件被包含在每个需要使用该函数的文件中。通过这样的方式,所有的文件都共享一个公共的函数声明。如果要修改该函数声明,涉及到的只需要在头文件修该即可。

函数声明通常放在C++程序的头文件中,供其源文件包含并作出定义。使用者只需要包含其头文件就可以使用函数定义的基本功能。基本函数声明实例如下所示。

int    Hex2Dec(char*str);           //声明十六进制转十进制函数,返回值类型为int,传入参数为char型字符串指针

该函数主要实现十六进制到十进制转换功能。其声明实例主要由三个部分组成,int为该函数需要返回值的类型,Hex2Dec为该函数名称(最好能实际代表函数功能的名称,使含义之一目了然)。最后小括号中的为参数列表,可以是传入的参数也可以是传出的参数,这里str为传入的字符串指针参数。

7.1.2  函数定义

函数声明主要是用来描述其基本情况,如函数的返回值、函数的参数列表以及函数名称等。那么函数定义主要是对利用解决某类功能的思路而实现的代码的封装,最终供需要使用者调用处理特定任务。函数定义基本语法格式如下所示。

返回类型  函数名(参数列表)

{

         …                                 //函数体

}

函数定义头部和该函数声明一样,由函数返回值类型、函数名以及参数列表组成。另外紧跟着函数头之后的一对大括号之内称为函数体。函数体中主要是由处理某种任务的算法语句组成。

函数声明中十六进制转十进制函数功能定义如下所示。

/*

* 函数功能:实现十六进制字符串转十进制函数功能

* 函数参数:传字符串指针型参数

* 返回值:返回整型值

*/

int  Hex2Dec(char*val)

{

         inti,data,value=0;                                 //定义三个整型变量,并且赋予value变量初值为0

         intlength = strlen(val);                         //调用库功能函数strlen计算传入的字符串长度,将结果赋给整型变量

         for(i=0;i<length;i++)                              //依据传入参数长度作为循环条件,逐位处理进制数转换

         {

                   data=val [i];                                  //将需要处理的字符串中位置的字符给整型变量data赋值

                   if((data>='a')&&(data<='f'))        //判断该字符的值是否在af的范围内,并作出处理

                            data=data-'a'+10;

                   elseif ((data>='A')&&(data<='F'))//
判断该字符的值是否在AF的范围内,并作出处理

                            data=data-'A'+10;

                   else

                            data-='0';                              //不在以上范围内,则默认直接减去相应0字符的值即可

                   value=value*16+data;                //最终将转换值放入变量value

         }

         returnsprintf(val,"%d",value);            //全部转换完毕后进行参数格式化,放回对应的字符串指针指向的空间中                                                                      //返回

}

上述函数定义解决了实现十六进制到十进制数转换的问题。整个处理的流程使用到了变量定义以及相应的循环结构控制。函数定义根据分析问题的需求,找到对应的解决问题算法,然后按步写出相应的执行语句,实现其功能。该实例程序初学者可以自己通过测试理解其算法。

7.1.3  函数的返回值类型

函数头的第一个组成部分为函数的返回值类型。函数的返回值类型可以是C++语言提供的基本数据类型,如intfloatdoublechar等。它还可以是由基本类型组成的复合类型,int*(指向整型指针)、char*(指向字符指针)string&等。它也可以是用户自己定义的类型,如结构体、类以及STL中容器类型等。最后如果函数不返回任何类型值,则使用关键字void表示即可。

下面可以参考几个函数返回值类型实例,了解下函数返回值类型基本情况。实例定义如下所示。

int  Hex2Dec(char*str);            //函数返回值为整型int

string getfileName();                            //函数返回值为字符串string

FileType getType();                     //函数返回值为枚举类型enum定义的变量FileType

void function();                             //函数不返回任何类型

标准的C++中,声明和定义函数必须给定一个返回值类型,不返回任何值即直接使用void即可,否则编译会出错。函数返回值类型不能是数组也不能是函数类型,但可以是返回数组的第一个元素的指针。另外,返回值类型前可以使用const修饰符,表示函数返回值不可被随意修改,适用处理数据需要保护的场合。

7.1.4  函数的参数表

函数作为编程接口,需要传入以及传出相应的数据,函数中的参数列表就是起到这种作用。参数列表摆放在函数名后的小括号中,不同的参数之间用逗号运算符隔开。函数参数可以是基本的数据类型、复合类型以及用户自定义类型等。

这里需要注意的是C++函数中参数之间不允许同名。函数的参数也并不是必须的,可以不需要任何参数,如直接置关键字为void表示该函数无任何参数。另外,函数声明时参数名不一定非要给出,只要给出类型名即可,声明如下所示。

int testCount(int,int);                 //声明函数testCount,该函数返回值为整型int,参数分别为两个整型数

在函数定义时候再给出参数名称就可以。但是作为一种好的编程风格,函数声明时也应该给出参数类型以及其名称。这样,程序代码的维护者可以清晰了解程序参数接口状况。

初学者在使用C++函数调用时会涉及到两个基本概念,分别是形参和实参。下面将会介绍两类参数在函数中的基本作用和区别,让初学者更好的理解函数。

1.函数形参

对于函数调用的参数列表来讲,存在两类参数形式。声明和定义函数时,用到的是函数的形式参数。形参通常在函数被调用的时候才会分配相应的内存单元,函数调用完毕之后所分配的形参空间会被释放掉。形式参数在函数调用周期中生命周期很短暂,只存在于调用前后过程中,下面将会通过一个简单的实例来演示形参使用情况。

int     testFun(int  a,int b);                
//
测试函数,该参数形参为整型变量ab

int main()

{

         intdata1,data2;

         data1= 12;

         data2= 14;

         testFun(data1,data2);                 //测试函数调用,实参为整型变量data1data2

}

int     testFun(int  a,int b)

{

         return(a+b);                                  //形式参数使用范围

}

上述实例中声明函数testFun()时,参数列表中的两个整型变量定义ab为形式参数,在函数定义的时候,函数内部也会使用形式参数ab来进行操作。

上述主函数main()内部,定义的两个整型变量data1data2为实参,通过调用函数testFun()传入两个实参,将实参值传给形参,在函数内部执行使用。一旦调用函数结束,分配形式参数的变量ab的内存空间将会被释放。

2.函数实参

函数实参是函数调用时传入函数的参数变量,该参数必须是确定的值。实参可以是常量、变量、表达式等,不管是何种类型需要注意的是实参必须是确定的值。上述形参实例中演绎了实参传递的过程,实参是有确定值的参数,因此在函数调用过程中只有实参通过传递送给形参,该顺序初学者必须要注意理解。

7.1.5  函数参数的类型检查

C++函数声明中讲述过,函数的声明其中一个优势就是在函数调用时会针对其参数作类型检查。C++一直被称为强类型语言,每个功能函数在调用时传入的实际参数(简称实参)在编译期间需要经过类型检查。如果实参和相应函数声明的形参类型不匹配,则会编译报错。但是有一类情况不容忽视,那就是类型的隐式转换问题。

通常情况下,执行算术运算时低类型可以转换为高类型。这样在转换之后,不会因为精度的损失对程序的处理产生影响。基本类型转换实例如下所示。

int function1(int,int);                    //声明函数function1,返回值类型为整型,参数分别为两个整型数

function1(2,3);                              //调用函数function1,传入两个整型实参

function1(2.32,3.23);                            //调用函数function1,传入两个double型实参

function1(“hello”,”C++”);            //调用函数function1,传入两个字符串型实参

上述实例中,声明函数function1()并且参数分别为两个整型数。在调用的时候,function1(2,3)传入两个整型实参,类型完全匹配,该函数调用不会出现问题。function1(2.32,3.23)传入两个double型的实参,此时编译器会将其隐式的转换为整型进行处理。通常此时编译器会给出一个警告信息,从doubleint型转换,降低了其计算精度,有可能会对数据计算造成影响。

function1(“hello”,”C++”)传入两个字符串实参,此刻传入的参数类型完全不匹配也不能进行隐式转换,所以通常编译器会报错,导致编译不能通过。隐式转换是一个比较棘手的问题,正常的软件开发中如果没有必要的话,最好不要使用能够引起类型隐式转换的操作。

7.2  参数传递

参数传递是C++中函数部分非常重要的概念。由于应用程序涉及大量的数据传输处理,而函数作为应用程序功能性接口,参数传递就是数据在功能函数的进出机制的实现。下面具体讲述参数传递机制在函数调用间的应用。

7.2.1  参数传递简介

参数传递本质上应该是被调用函数和调用者之间发生的数据通信。函数的参数通过栈空间进行传递。栈是一种先进后出型的数据结构,函数的调用过程与其有相当大的关联。本章主要讲述C++语言使用基本概念。如果读者想深入了解,可以查阅相关的资料。

通常C++函数的参数传递包含三种方式,即传值方式、引用传递方式和指针传递方式。下面针对三种传递方式逐一讲述。

7.2.2  传值方式

函数调用中,参数传值方式是指传入的是变量值的拷贝,即把一个实参值的副本赋给函数的形参,在函数体中的任何处理都不会影响到实参的变量。C++中,函数调用默认传递方式为传值方式。当函数调用按值传递时,函数访问不到当前调用时的实参。因为它访问处理的值是该调用时的实参拷贝。这些值存储在相应的栈空间中,所以修改这些值不会影响实参的值。可以通过一个实际运行的实例理解参数传值方式。

1.准备实例

打开UE编辑器,新建空白文件另存为chapter0701.cpp。随后连同makefile编译文件一起通过FTP工具传输至Linux服务器。客户端通过scrt访问操作。实例程序代码编辑如下所示。

/**

* 实例chapter0701

* 源文件chapter0701.cpp

* 传值参数方式实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:实现变量值交换功能的函数声明

* 函数参数:传入两个整型参数

* 返回值:空

*/

void swap(int a,int b);

int main()

{

         inti = 2;                                                             //定义整型变量i,赋初值2

         int j= 3;                                                             //定义整型变量j,赋初值3

         cout<<"beforeswap():"<<i<<" "<<j<<endl;         //输出没有转换前的变量ij的值

         swap(i,j);                                                          //调用转换变量值函数,传入实参ij的值

         cout<<"afterswap():"<<i<<" "<<j<<endl;    //输出交换后的实参变量ij的变量值

         return0;

}

//实现变量值交换功能的函数的定义

void swap(int a,int b)

{

         inttemp = b;                                                    //定义临时变量temp,将参数传入的变量值赋给temp

         b =a;                                                                 //将参数变量a的值赋给变量b

         a =temp;                                                          //将临时变量temp的值赋给变量a

}

上述实例主要演示通过函数传值方式交换变量值的功能。本程序主要包含主函数与交换函数两个部分定义,交换函数中通过传入整型变量值内部实现交换,具体程序分析在后面讲解。

2.编辑makefile

Linux系统平台下需要编译的源文件为chapter0701.cpp,相关makefile工程文件编译命令编辑如下。

OBJECTS=chapter0701.o

CC=g++

 

chapter0701: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0701

clean:

    rm -fchapter0701 core $(OBJECTS)

submit:

    cp -f -rchapter0701 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该程序文件运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0701.o chapter0701.cpp

g++ chapter0701.o -g -o chapter0701

[developer @localhost src]$ make submit

cp -f -r chapter0701 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0701

before swap():2 3

after swap():2 3

该程序一直都是解释参数传值方式的经典了。程序主要功能用于转换实参对应变量中的值,但是在参数传值的方式下,由于实参传入是一份拷贝值,所以函数内部使用的形参中的值是实参ij变量值的拷贝,所有的针对形参的操作都不会对实参变量产生影响,所以运行结果自然是没有实现其理想中的功能。

7.2.3  引用传递方式

C++传值方式由于只传递实参的值得拷贝,所以在函数调用时需要改变实参的情况下该方式不符合实现要求。接下来讲述C++的第二种参数传递方式,引用传递。参数的引用传递方式,是将函数声明时的形参声明成引用的方式。这种方式改变了程序默认的传值方式参数的传递机制。当形参为引用传递的时候,函数调用时接受的是实参的地址,也就是函数中操作形参时会知道实参在内存中的地址的位置。所以函数内部操作可以改变实参的值,也可以取得实参的地址。

函数参数声明为引用参数,必须使用C++&引用标识符。上述的实现实参值转换的函数声明可以定义为如下的方式。

void swap(int &a,int &b);            //声明函数swap,不返回任何类型值,即使用void关键字,参数定义为引用方式

调用该函数时,根据传入实参给形参赋值,引用传递的方式使得函数的内部进行的值的交换处理的参数操作,直接修改了实参的地址。所以按照引用参数传递的方式可以实现真正交换变量值的期望功能。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0702.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0702

* 源文件chapter0702.cpp

* 引用参数传递方式实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:实现变量值交换功能的函数声明

* 函数参数:传入两个整型引用参数

* 返回值:空

*/

int swap(int &a,int &b);

/*主程序入口*/

int main()

{

         int i= 2;                                                                      //定义整型变量i,赋初值2

         int j= 3;                                                                      //定义整型变量j,赋初值3

         cout<<"beforeswap():"<<i<<" "<<j<<endl;                  //输出没有转换前的变量ij的值

         swap(i,j);                                                                   //调用转换变量值函数,传入实参ij的值

         cout<<"afterswap():"<<i<<" "<<j<<endl;             //输出交换后的实参变量ij的变量值

         return0;

}

int swap(int &a,int &b)                                                      //实现变量值交换功能的函数的定义

{

         inttemp = b;                                                              //定义临时变量temp,将参数传入的变量值赋给temp

         b =a;                                                                          //将参数变量a的值赋给变量b

         a =temp;                                                                   //将临时变量temp的值赋给变量a

}

上述实例主要演示通过函数引用方式交换变量值的功能。本程序主要包含主函数与交换函数两个部分定义,交换函数中通过传入整型引用变量内部实现交换,具体程序分析在后面讲解。

2.编辑makefile

Linux平台下需要编译的源文件为chapter0702.cpp,相关makefile工程文件编译命令编辑如下。

OBJECTS=chapter0702.o

CC=g++

 

chapter0702: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0702

clean:

    rm -fchapter0702 core $(OBJECTS)

submit:

    cp -f -rchapter0702 ../bin

    cp -f -r *.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行可执行文件程序运行如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0702.o chapter0702.cpp

g++ chapter0702.o -g -o chapter0702

[developer @localhost src]$ make submit

cp -f -r chapter0702 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0702

before swap():2 3

after swap():3 2

参数引用传递的方式除了可以直接操作实参的地址以外,还有一个好处就是解决了在传递大对象时的性能问题。通常参数的传值方式在不必须作实参转换等操作的情形下,基本能满足函数作为接口的数据通信。但是一旦传递大型类对象时,由于形参得到的是实参的值的拷贝,所以在函数每次调用的时候实参都会被拷贝,在函数频繁被调用过程中性能就会受到一定的影响。

引用传递函数内部操作可以直接访问实参类型的对象,并不需要在函数调用过程中执行拷贝操作。但是引用传递中由于函数的操作有可能会无意修改实参的值,会给程序处理带来一定的危险。在这种情况下通常一个比较好的方式就是在函数声明中引用参数定义前加上const关键字,限制该实参在函数中被修改的可能。

通常在软件开发中,函数的参数声明为传值方式还是引用方式需要根据实际的需要仔细的分析,给出比较合理的定义方式。通常在软件开发中,当传递的数据类型对象不大或者是C++内置的数据类型对象时,使用传值方式是适合的。当传递的数据类型为大型对象时,可以考虑使用引用传递的方式。如果不期望实参在函数中被修改的话,可以在引用参数定义前加上const修饰符即可。

7.2.4  指针传递方式

C++中函数调用可以使用传值和传递引用方式。那么用户同样也可以另外一种方式通过指针传递的来传递参数。众所周知,指针是一种类型变量,它指向其它变量的地址即也称为地址变量。指针本身是一种类型,指向了存储对应变量的地址。所以指针传递方式是将实参的地址传递给了形参,在函数内部的代码就可以操作实参的地址,从而改变函数外部的实参了。

上述实参变量交换值得实例同样可以采用指针参数传递的方式来实现,代码实例如下所示。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0703.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0703

* 源文件chapter0703.cpp

* 指针参数传递方式实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:实现变量值交换功能的函数声明

* 函数参数:传入两个整型指针参数

* 返回值:空

*/

int swap(int *a,int *b);

/*主程序入口*/

int main()

{

         inti = 2;                                                                      //定义整型变量i,赋初值2

         intj = 3;                                                                      //定义整型变量j,赋初值3

         cout<<"beforeswap():"<<i<<" "<<j<<endl;                  //输出没有转换前的变量ij的值

         swap(&i,&j);                                                              //调用转换变量值函数,传入实参ij的地址

         cout<<"afterswap():"<<i<<" "<<j<<endl;             //输出交换后的实参变量ij的变量值

         return0;

}

int swap(int *a,int *b)                                                                 //实现变量值交换功能的函数的定义

{

         inttemp = *b;                                                            //将指针形参b对应的值赋给临时变量temp

         *b =*a;                                                                       //将指针形参a对应的值赋给指针变量b

         *a =temp;                                                                          //将临时变量中的值赋给指针变量a

}

上述实例主要演示通过函数指针传递参数方式交换变量值的功能。本程序主要包含主函数与交换函数两个部分定义,交换函数中通过传入整型指针变量内部实现交换,具体程序分析在后面讲解。

2.编辑makefile

Linux平台下,需要编译的源文件为chapter0703.cpp,相关makefile工程文件编译命令编辑如下。

OBJECTS=chapter0703.o

CC=g++

 

chapter0703: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0703

clean:

    rm -fchapter0703 core $(OBJECTS)

submit:

    cp -f -rchapter0703 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该可执行程序运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0703.o chapter0703.cpp

g++ chapter0703.o -g -o chapter0703

[developer @localhost src]$ make submit

cp -f -r chapter0703 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0703

before swap():2 3

after swap():3 2

同样执行对应的编译命令生成可执行程序,运行该执行程序会得出和引用传递同样的结果。这里初学者可以自己编辑该程序实例,通过执行该程序来验证。指针参数传递要求函数调用时,传入的应该是实参变量对应的地址,即使用取变量地址符号&即可。

函数声明中形参都定义为指针变量,在函数调用时实参的地址赋给形参对应的指针。这样等于修改了形参指针的指向,指向了实参的地址。对于指针变量取其值得符号为*,也称为解引用符号,用来取指针变量对应的值。该实例中函数体中的变量值的交换就采用这样的赋值方式。指针参数传递的方式在很多情况下也被大量的使用。通常仔细研究C++标准库中提供的库函数接口,一般都采用指针作为参数传递的方式。这是因为指针作为参数传递会为函数体定义带来更加方便的操作

7.2.5  数组参数

函数调用过程中有时候不可避免需要传递数组或者比如STL库中封装的向量、列表等数据。数组是同类型数据的集合,C++中数组作为参数传递使用的方式不是传值而是采用指针参数传递的方式。数组的数组名可以看作是指向数组的首个元素的指针,通过指针参数传递数组名即可在函数内部操作数组实参的实际内容了。

下面可以通过一个实例来理解下数组作为参数在函数调用过程中传递的方式,实例演示如下所示。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0704.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0704

* 源文件chapter0704.cpp

* 数组参数传递方式实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:计算数组中元素的值

* 函数参数:传入整型数组指针参数和数组的长度参数

* 返回值:空

*/

void calculateArray(int *array,int len);

/*主程序入口*/

int main()

{

         inttest_array[10] = {1,2,3,4,5,6,7,8,9,10};           //定义10个整型元素数组,并且对其元素初始化值

         calculateArray(test_array,10);                              //调用计算数组元素值的函数,传入数组名及其数组元素长度

         cout<<"Arrayelement's value:"<<endl;               //打印输出数组计算后的元素值

         for(inti = 0;i < 10;i++)                                              //执行循环结构

         {

                   cout<<test_array[i]<<"";                                //输出单个数组元素,元素之间使用空格隔开

         }

         return0;

}

void calculateArray(int *array,int len)                                     //计算数组中元素值的函数定义

{

         intlength = len;                                                                 //定义临时整型变量length,将形参len值赋给它

         for(inti = 0;i < len;i++)                                             //根据传入的数组长度执行for循环结构

         {

                   array[i]= array[i]*i;                                          //将数组中每个元素乘以其对应的位置值放入对应数组中

         }

}

上述实例主要演示通过函数数组参数方式根据计算规则计算其中元素值的功能。本程序实例主要由主函数与计算数组元素函数组成。计算数组函数内部通过遍历数组元素实现了计算功能,具体程序分析在后面讲解。

2.编辑makefile

Linux平台下需要编译的源文件为chapter0704.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0704.o

CC=g++

 

chapter0704: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0704

clean:

    rm -fchapter0704 core $(OBJECTS)

submit:

    cp -f -rchapter0704 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至bin目录,执行该可执行程序运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0704.o chapter0704.cpp

g++ chapter0704.o -g -o chapter0704

[developer @localhost src]$ make submit

cp -f -r chapter0704 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0704

Array element's value:

0 2 6 12 20 30 42 56 72 90

由于数组名即为指向数组第一个元素的指针,所以实参以数组名传递就等于是指针参数传递的方式,将数组首地址传给形参在函数中操作。自然在函数中操作形参对应的值会修改实参数组对应实际内容了。实例中功能函数声明时,参数定义为一个指针类型和整型变量,指针类型变量表示数组首地址,整型变量表示数组的元素长度。主程序中调用该函数时,传入实参对应的数组首地址即数组名和对应的长度,在功能函数定义中通过循环操作修改了数组中元素的值。通过在主程序外打印实参数组对应的元素值,验证了函数中元素的操作修改直接影响到了实参数组内容。

7.3  函数重载

函数重载即同一个函数名对应着多个函数功能的实现,这些函数功能大致上应该是相近的。C++中函数重载定义,由编译器根据传入的参数类型以及参数个数来决定在函数调用时执行哪个函数代码即采用哪个函数实现。

在软件开发中,经常需要实现一类功能。这些功能实现基本的思路是一致的,正常情况下开发者会通过定义实现不同函数来实现。意味着这要为每个功能实现的函数取一个名字用来区别定义,在该类功能函数调用时需要管理一批不同函数名的函数。在一定程度上这为开发者制造了一定的麻烦,为了解决这类情况的出现,C++提出了函数重载的概念。采用一个函数名称通过不同的参数类型以及参数个数让编译器来决定在实际调用函数过程中执行哪个函数实现。以下是一部分重载函数声明实例。

int addValue(int value1,int value2);                     //声明函数addValue,传入两个整型变量参数,返回整型值

double addValue(double a,double b);                //声明函数addValue,传入两个double型参数,返回double型值

int addValue(int i,int j,int k);                                   //声明函数addValue,传入三个整型变量参数,返回整型值

double addValue(double value3,double value4,doublevalue5);      //声明函数addValue,传入三个double型参数,返回double型值

以上四个同函数名的函数声明,通过其函数的声明中参数的类型以及个数的不同来实现函数重载。编译器会根据函数参数表中参数的个数或者类型的不同,判别函数之间是否实现重载以及选择哪个函数作为调用的实现。

下面通过完善addValue函数重载实例来理解下C++下函数重载的使用,实例演示如下所示。

1.准备实例

打开UE工具,创建新的空文件并且另存为chapter0705.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

/**

* 实例chapter0705

* 源文件chapter0705.cpp

* 函数重载实例

*/

#include <iostream>

using namespace std;

/*

* 函数功能:计算两个整型值相加

* 函数参数:传入两个整型变量参数

* 返回值:返回整型变量值

*/

int addValue(int value1,int value2);

/*

* 函数功能:计算两个double型值相加

* 函数参数:传入两个double型变量参数

* 返回值:返回double型变量值

*/

double addValue(double a,double b);

/*

* 函数功能:计算三个整型值相加

* 函数参数:传入三个整型变量参数

* 返回值:返回整型变量值

*/

int addValue(int i,int j,int k);

/*

* 函数功能:计算三个double型值相加

* 函数参数:传入三个double型变量参数

* 返回值:返回double型变量值

*/

double addValue(double value3,double value4,doublevalue5);

/*主程序入口*/

int main()

{

         cout<<"Thesum:"<<addValue(2,3)<<endl;                          //打印两个整型数相加结果

         cout<<"Thesum:"<<addValue(2.3,3.2)<<endl;                    //打印两个double型数相加结果

         cout<<"Thesum:"<<addValue(2,3,4)<<endl;                       //打印三个整型数相加结果

         cout<<"Thesum:"<<addValue(1.2,2.3,3.2)<<endl;             //打印三个double型数相加结果

         return0;

}

/*计算两个整型值相加函数定义*/

int addValue(int value1,int value2)

{

         returnvalue1 + value2;                                                             //返回两个整型值相加结果

}

/*计算两个double型值相加函数定义*/

double addValue(double a,double b)       

{

         returna + b;                                                                                 //返回两个double型值相加结果

}

/*计算三个整型值相加函数定义*/

int addValue(int i,int j,int k)

{

         returni + j + k;                                                                             //返回三个整型值相加结果

}

/*计算三个double型值相加函数定义*/

double addValue(double value3,double value4,doublevalue5)

{

         returnvalue3 + value4 + value5;                                            //返回三个double型值相加结果

}

本实例程序主要通过函数重载的方式演不同类型变量加法功能。本程序主要由主函数与四个重载的加法计算函数组成,通过不同的参数变量类型以及不同的参数个数实现函数的重载。具体程序分析在后面讲解。

2.编辑makefile

Linux平台下需要编译的源文件为chapter0705.cpp,相关makefile工程文件编译命令编辑如下所示。

OBJECTS=chapter0705.o

CC=g++

 

chapter0705: $(OBJECTS)

    $(CC)$(OBJECTS) -g -o chapter0705

clean:

    rm -fchapter0705 core $(OBJECTS)

submit:

    cp -f -rchapter0705 ../bin

    cp -f -r*.h ../include

上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

3.编译运行程序

当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交该可执行文件至本实例bin目录,通过cd命令定位至bin目录,执行该可执行程序运行结果如下所示。

[developer@localhost src]$ make

g++ -c -o chapter0705.o chapter0705.cpp

g++ chapter0705.o -g -o chapter0705

[developer @localhost src]$ make submit

cp -f -r chapter0705 ../bin

cp -f -r *.h ../include

[developer @localhost src]$ cd ../bin

[developer @localhost bin]$ ./chapter0705

The sum:5

The sum:5.5

The sum:9

The sum:6.7

上述重载实例重载实现了不同类型变量值相加的函数功能,使用相同的函数名。通过传入参数的类型以及个数的不同,函数在调用时决定使用重载中的哪个函数来执行并返回结果。函数重载在C++中有很重要的作用,在后续的C++面向对象的思想中。函数的重载也是实现多态的方式之一,会在后续章节中作详细讲述。

7.4  内联函数

C++中提供一种提高函数执行效率的方法,那就是定义内联函数。内联函数通常被认为替代C语言中宏定义使用的一种更安全有效的方式。函数一旦被定义为内联的方式,编译器在对其参数类型等安全检查过后,会使用内联函数定义的代码直接替换函数调用,省去这部分在栈中的开销。

内联函数通过关键字inline来区别与普通函数的定义。通常inline关键字放在函数头首部中间通过空格隔开。但是这里需要注意的是内联函数关键字比如要在函数定义时加上才能表明该函数是内联函数,当然也可以声明和定义都加上该关键字。下面实例可以说明其使用方式。

inline int addValue(int value1,int value2);                   //声明两个整型值相加计算函数为内联函数

inline int addValue(int value1,int value2 )                   //定义内联函数

{

         returnvalue1 + value2;

}

需要注意的是addValue函数要定义为内联函数,则关键字必须要放在函数定义的函数头前面,声明时候的inline关键字可有可无。

内联提高了函数执行的效率,但是在实际使用时需要根据具体情况分析是否适合使用它。通常内联函数是在调用处展开该函数定义的代码供使用,这是种典型的牺牲空间换效率的作法。这种方式在软件开发很多情况下都会使用到。但是这里函数内联只适合实现功能的短小代码的函数,并且在使用过程中是调用频繁的,这样才能体现出内联的优势。否则正常情况下不要去滥用,导致程序更高的效率代价的付出。

抱歉!评论已关闭.