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

C++ Primer学习总结 第6章 函数

2015-03-07 00:48 357 查看
第6章 函数

1. 函数最外层作用域中的局部变量不能使用与函数形参一样的名字,因为它们属于同一个作用域范围.




2. 局部静态变量的生命周期: 在整个程序的执行路径第一次经过对象定义语句时初始化,并且直到整个程序终止时才被销毁,在此期间即使对象所在函数结束执行也不会对它有影响.




3. 如果重载的函数的参数只有顶层const区别,那么是错误的:



如果有底层const区别可以算作重载.

4. 如果函数的参数要使用引用(且不会改变引用对象的值),那么应该定义成常量引用. 因为使用普通引用参数会限制函数所能接受的实参类型.



上述例子,如果传递常量int给函数get(),那么就将出错.
还有一个更难察觉的错误,如果A函数正确定义形参为常量引用,但是B函数依然定义形参为普通引用,那么在A函数中使用B函数将出错:




5. 如何给函数传递数组形参?
数组也是对象,只不过数组名会自动转化为首元素指针.数组也是有地址的.数组也是有指针的.数组的指针用*解引用就可得到数组本身.
一维数组:



多维数组: C++实际上没有多维数组,所谓多维数组不过是数组的数组,所以多维数组的第2维(以及后面所有维度)的大小都是数组类型的一部分,不能省略.



问题:为什么上面输出A数组的值要用(*A)[i],而不能直接A[i]输出?
A是一个指向int[10]数组的指针,那么*A就是一个int[10]的数组.那么(*A)[i]就是这个int[10]数组的第i个元素了.

或者可以这么理解其实A是二维数组int a[10][10]的这个a数组中的第一个元素(该元素是一个int[10]数组)的指针,所以*A是a的第一个元素(该元素是一个int[10]数组),所以(*A)[i]是具体的int值元素了.

传到print()函数中的是a数组首元素(a[0]数组)的指针,所以如果想输出a[1]的10个元素,需要先A+1,然后*(A+1),然后取每个元素(*(A+1))[i].





6. 深入讨论:首先使用数组名时,会自动将该数组名转换为该数组首元素的指针. 但是如果我们想使用该数组的指针(而不仅仅是首元素的指针)该怎么办?
数组也是对象,对其用&取地址符就可以获得它的地址:




7. 对于char **A 这种形参,它的含义是什么?
下面的话“A是一个数组,该数组内都是char*指针”不严谨。正确的理解应该是A是一个指向char类型指针的指针。所以我们*A可以得到一个char*指针,至于这个char*指针到底指向一个char数组的元素还是该char*指针仅仅指向一个char还是该char*指针内容根本非法,就不是我们能管的事了,这需要程序的其他部分给予保证。



注意上面程序的实现.

8. 如何给函数传递数组引用形参?



数组引用形参要正确写明数组的所有维度.

9. 不要返回局部变量的引用或指针(虽然测试简单的程序,这么做经常能得到正确的结果,但是这么做却是严重的错误):





10. 调用运算符和运算符,箭头运算符优先级相同且都符合左结合律,所以如果函数返回指针,引用或类的对象,我们能用函数调用的结果访问结果对象的成员.




11. C++11支持列表初始化返回值:




12. 函数不能返回数组(因为数组不能被拷贝),函数只能返回数组的指针:




13. 如何直接定义一个返回数组指针的函数?



上面的括号不能少.

14. const_cast用法:
用法:const_cast<type_id>(expression)
该运算符用来修改类型的(底层)const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的
一、常量指针被转化成非常量的指针,并且仍然指向原来的对象;
二、常量引用被转换成非常量的引用,并且仍然指向原来的对象;



由上面s3可知const_cast<>()强转还是保持以前的内存地址,





const_cast<>()可以使得底层const消失,但是内存地址还是不变的.用了const_cast<>()之后就可以通过b改变之前内存的内容,但是依然不能通过a去改变. 引用和指针的底层const用强转都是一样的效果.

如果仅用const_cast<>()强转顶层const是无意义的行为,且编译不能通过.



本来就是用a的值去赋给b, 就算a转成int类型,也没有任何区别.
error: invalid use of const_cast with type'int', which is not a pointer, reference, nor a pointer-to-data-member type.
上面是编译器的错误说明. 说明const_cast只能用于指针或引用的底层const.(第三项不知道是什么?)

15. 如何声明仅具有局部作用域的函数?



在全局定义,但是在局部作用域声明函数,即可使得特定函数仅具有局部作用域.main中对于print不可见,因为print的定义在main函数后面,且print的声明在get函数中. 如果print定义在main前面,就算print不声明,main中也可以使用print.

16. 函数默认实参,如果函数某个形参有默认值,那么它后面的每个形参都必须有默认值. 下面是两种函数定义默认值的方式:
方式一:推荐此方式.



方式二



且如果调用含有默认值的函数,所给出的实参总是优先匹配最左边的形参.
局部变量不能作为默认实参.


17. 只能用全局变量作为默认实参.且默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时.




18. const与constexpr 修饰符:
我们已经知道了字面值常量的值是不可更改的,所以称之为常量。变量是内存中的一块存储空间,我们可以读写里面的内容。但有时候我们也需要一种机制能够阻止随意的修改一个变量的值,这时候我们就要用到const限定符(const-qualifier)。在定义或者声明一个变量的时候,const限定符可以放在我们前面讲过的类型说明符前面或者后面表示该变量的值不可被更改。要注意的是,如果变量定义或者声明的时候有const限定符,那么一定要进行初始化。因为const变量不可以后来被赋值。对基本数据类型来说,这也是初始化和赋值的一个细微差别:可以初始化一个const变量,但不可以赋值。例如,



const限定符在语法上也是类型说明符的一种,但是他不可单独使用,必须和其他的类型说明符搭配使用而表明不可改变(constant)的性质。因为const限定符也是类型说明符,所以const变量和非const变量在C++中并不是同一种类型,虽然他们之间还是有着千丝万缕的联系,我们以后会详细讲述。你现在要记住的便是int和const
int并不是同一种类型。

C++还提供了一种更为``严格''的constexpr说明符(constexpr-specifier),在变量被声明或者定义的时候,如果类型说明符前面或者后面用了constexpr说明符,则表明该变量是一个const变量同时该变量必须被初始化。你也许说,这个const不是一样么constexpr更加严格,用来初始化该变量的表达式必须是一个``常数表达式''。也就是说constexpr变量是一个由常数表达式初始化的const变量。关于常数表达式,我们以后还会详细谈起,对于基本数据类型来说,常数表达式是由字面值常量,constexpr限制的变量,和由常数表达式初始化过的const变量组成的表达式。换句话说,常数表达式的值在编译期间就可以确定。



constexpr的变量的值必须是编译器在编译的时候就可以确定的。上例中因为nonconst_var的值在语法上来讲,运行期间可能被更改,所以编译期间无法确定,不属于常数表达式。因为const_var2是由非常数表达式来初始化的,所以const_var2也不是常数表达式。但const_var2本身的声明,定义及初始化是合法的。constexpr比const更严格,用来初始化constexpr_var2和constexpr_var3的也都不是常数表达式,所以他们的定义都是错误的。

19. 函数指针及其用法:



当把函数名作为一个值使用时,该函数自动转化为指针.

声明具有函数指针形参的函数:



使用typedef简化函数指针的定义:



C++ primer 第5版中文版 P222-223页有更多内容展开.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: