您的位置:首页 > 编程语言 > C语言/C++

C++学习第8篇-函数深入

2012-02-29 11:39 232 查看
1. 函数的形参和实参

1)形参和实参

通常的使用,函数参数(形参)和函数自变量(实参)是互通的。

函数形参-在函数原型和函数声明中的变量:



函数实参-函数调用时,传入代替函数形参的变量;



注意:函数的形参只有局部变量的范围,函数调用结束,形参自然销毁;

3种传参方式:传值、传引用和传地址

2. 值传递

值传递-将实参赋值给函数执行;



因为只是传递一个实参的拷贝,函数不能修改原来实参的值;



值传递的优点:

A)可以传递变量、数值和表达式;

B)参数不会被修改;

缺点:传递如结构体、类,需花费很长的时间;

在多数情况下,值传递是最好的函数参数传递方式,灵活和安全。

3. 引用传递

值传递的返回值,只能通过函数的return来实现;

在多数情况下,值传递是合适的;但如需改变参数的值,如重排列一个数组时,修改实参是有效和清晰的;

引用传递,就可以修改参数的值;



再如:



函数一般只有一个返回值,如需返回多个值,只需传递多个引用即可:



2)常量引用传递

引用传递,没有值传递时结构体或者类对象时耗时;引用传递创建真正的实参,不拷贝任何信息;

引用传递中,函数可以修改参数的值;如果不允许修改,可以采用常量引用才传递:



上面例子会出现编译错误的!

使用常量引用传递的几个原因:

A)协助编译器保证不能改变的参数不会修改;B)告诉编程人员留意参数是否会被修改;C)帮助编程人员调试错误值。

记住:通常采用常量引用传递,除非你确实需要改变参数的值。

引用传递的优点:

A)允许函数修改参数的值,有时非常有用的;B)引用不涉及到复制参数的信息,所以较快;

C)可以传递常量引用,防止无目的的修改; D)可以返回多个值。

缺点:

A)非常量引用不能是数值和表达式,只能是变量;B)比较困难清晰说明传递的引用是输入、输出还是两者皆是;

C)不能从函数调用确定参数是否被修改;

引用传递比值传递速度较慢!

4. 地址传递

地址传递是另一种变量传递方式,只传递变量的地址,而不是变量本身;函数形参必须是指针。



地址传递的典型应用在内存动态分配和数组访问:



以上例子中,必须传入数组的长度。

通常,在解引用地址传递的参数之前,最好对其进行空指针判断:



地址传递的优点:

A)允许函数改变参数的值;B)因为不进行拷贝,速度比较快;C)可以返回多个值。

缺点:A)参数不能为数值或表达式,必须是正常的变量;C)所有参数必须检查是否为空指针;D)较值传递慢。

引用传递通常比地址传递安全,引用传递是多数时候使用的方式。

2)值传递、引用传递和地址传递没有很大的区别:

A)引用传递,在编译器处理时,只作为指针处理;

B)和指针和引用传递的主要区别是引用传递有比较清晰和严格的语法;所以引用传递更安全,但相对不够灵活;但在效率上是不相上下的。

D)地址传递只是传递地址的地址值,若函数改变此地址值,只是改变了临时值,没有改变地址的实际值。



尽管地址传递实际上是值传递,但依然可以修改传递地址的存储的值;这时引用传递、地址传递和值传递的区别。

若需通过地址传递来修改参数的实际指向地址,可以传递地址的引用:



结论是:引用是指针,指针的地址是值传递。

5. 返回值是值、引用和地址

值、引用和地址的返回给函数调用几乎是一样的。

1)值返回是最简单和最安全的方式:

返回的变量或表达式可以包含函数声明的局部变量;

对于结构体和类对象是比较慢的;

2)引用返回

引用返回必须是变量,调用者可以继续使用该引用修改变量,很多时候是比较有用的,而且比较快速;

注意不能返回函数的局部定义的变量引用:



3)地址返回

地址返回只能返回变量的地址,不能是数值和表达的;

地址返回也是比较快速的,不能返回局部变量的地址;

若返回的是调用函数的局部定义变量的地址,编译器会产生警告信息的;

地址常用之一就是返回新的分配内存给调用者:



小结:

一般值返回是足够使用的,足够灵活和安全;引用返回和地址返回是比较有用的,特别是针对结构体和类对象的动态分配地址;

注意在引用返回和地址返回时,确保返回的不是函数自定义的局部变量的引用或地址。

6. 内联函数

函数的使用有几大好处:函数代码可重用;比较容易改变和更新代码;使代码更易读和易写;提供了类型检查;

每次的函数调用,往往需要付出更多的操作;就地编码较好;

就地编码即调用函数时,直接将代码嵌入当前位置;这是所谓的内联函数,使用关键字inline

内联函数比较适合短函数,特别是在循环内,而没有分支。

7. 函数重载

函数重载是C++一大特性,允许创建不同的函数使用相同的名称,而使用不同的参数;



注意函数的返回类型不能作为函数重载的标志;使用typedef不能理解为创建一个新的类型;

匹配重载函数可能出现的3中情况:

1) 找到匹配函数;2) 没有找到匹配函数;3) 多个重载函数符合条件(歧义匹配)。

匹配重载函数的过程:

1)寻找最合适的;2)通过合理转换的匹配(参数的升级转换,如char->int);

3)通过强制的标准转换(如int->double);4) 当以上都没有找到,C++会遍历用户自定义函数;

歧义匹配属于编译运行错误。

处理歧义匹配:

1)定义一个新的重载函数;

2)显式转换歧义的参数到确定的类型;

小结:函数调用会明显降低函数的灵活性,同时附带少许的风险;函数重载或许比较复杂,但非常实用。

8. 默认参数

默认参数,即函数的形参已经赋予初始值。



默认参数是非常出色的选择,当函数需要的默认的值,用户可以也不需要修改;

注意:多于多个默认值,不能跳过赋值;

A) 所有的默认参数必须是由右到左的;B) 最右边的默认值是最常覆盖修改的;C) 默认值不能作为函数重载的标志。

9. 函数指针

函数指针是比较高级的话题;对于数组名是一个地址,那么函数名可以理解是函数的地址;

函数调用时,首先解引用函数名(即函数地址),获取函数的地址入口,才开始执行;

函数指针即指针指向函数的地址,函数指针可以重定向:

因为()的优先级比*高,所以函数指针必须加括号:



注意:函数指针的签名(包括参数、返回值等)必须匹配指向的函数的签名。

函数指针调用,就如指向的函数的隐式调用一样,如pFoo() ;

函数指针的通常应用是函数实现特定的功能,但你想用户可以自定功能的实现;

如选择排序:





10. 栈和堆

程序使用的内存区域分4个部分:

A)代码区:编译的程序放置的地方;B)全局区:存放全局变量的地方;C)堆:动态分配的变量存放地方;D)栈:形参和局部变量存放的地方。

1)堆-自由存放区,是动态分配的一个巨大缓冲池;



必须知道:顺序的存储请求,不一定得到实际上的顺序存储区域;

当动态分配的内存释放,返回堆,作为新的可以分配的内存;

2)栈:只能从顶部取出和放入的操作:

top()、pop()、push()是常用的操作;

栈是first in, last out(FILO)的结构;

当栈的的存储区域已经满了,就会出现溢出。

不必要详细了解栈的工作细节。

11. 递归

C++的递归就是函数的自身调用。

递归必须有终止条件,否则就为无限递归。如:



再如:求1到nValue的和:



示例3:Fibonacci数:





12. 命名空间

命名空间概念,确切来说不归入函数这部分,但是十分重要的概念。

程序中出现标识符(变量或函数名)在相同的范围,即命名冲突。

这时,可以使用命名空间来解决,通过全局::运算符,来调用所需函数或变量;

亦可以通过using关键字,引入命名空间。

13. 捕捉错误

错误包括断言、错误、退出和异常。

编程中,你不可能避免错误,包括语法和词义错误;

语法错误-没有按照相应的语言的语法格式编程;

词义错误-程序没有按照编程人员的意愿,实现目标;不能被编译器捕捉,

A)逻辑错误;B)冒犯性假设错误。

处理:

A)防御错误;B)探测错误;

2)断言-Assert

如果断言的条件是true,程序不过处理,如果断言的条件是false,则程序发出错误异常消息并终止程序;

至此,C++面向过程编程已学习完毕,后面部分为C++面向对象编程的学习。Have Fun!!!!!!!

【免责特此声明:

1)本内容可能是来自互联网的,或经过本人整理的,仅仅代表了互联网和个人的意见和看法!

2)本内容仅仅提供参考,任何参考该内容造成任何的后果,均与原创作者和本博客作者无关!】
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: