博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++ primer 5th 笔记:第六章
阅读量:7064 次
发布时间:2019-06-28

本文共 6197 字,大约阅读时间需要 20 分钟。

第六章:函数

  笔记

    1. 通过调用运算符(call operator)来执行函数。调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是一个函数或指向函数的指针

    2. 在c++语言中,名字有作用域,对象有生命周期。

       a. 名字的作用域是程序文本的一部分,名字在其中可见。

       b. 对象的生命周期是程序执行过程中该对象存在的一段时间。

    3. 函数体是一个语句块,块构成一个新的作用域。

    4. 形参和函数体内部定义的变量统称为局部变量(local variable)。

    5. 局部静态对象(local static object)在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁。

    6. 函数只能定义一次,但可以声明多次。函数的声明也称作函数原型

    7. 每次调用函数时都会重新创建它的形参,并用传入的实参对形参进行初始化。

    8. 在c++语言中,建议使用引用类型的形参替代指针

    9. 使用引用避免拷贝。拷贝大的类类型对象或者容器对象比较低效,甚至有的类类型(包括IO类型在内)根本就不支持拷贝操作

    10. 如果函数无须改变引用形参的值,最好将其声明为常量引用

    // 不良设计:第一个形参的类型应该是const string&    string::size_type find_char(string &s, char c,                                             string::size_type &occurs);    // 只能将find_char函数作用于string对象。    find_char("Hello World", 'o', ctr);    // 错误    // 尽量使用常量引用

    11. 可以使用非常量初始化一个底层cosnt对象,但是反过来不行。

    12. 数组的两个特殊性质对我们定义和使用作用在数组上的函数由影响:a. 不允许拷贝数组;b. 使用数组时通常会将其转换成指针。

    13. c++语言允许将变量定义成数组的引用,形参也可以是数组的应用。

    // 正确:形参是数组的应用,维度是类型的一部分    void print(int (&arr)[10])   // &arr两端的括号必不可少    {        for (auto elem : arr)            cout << elem << endl;    }

    14. 传递多维数组。数组的第二维的大小都是数组类型的一部分,不能省略

    // matrix 指向数组的首元素,该数组的元素是由10个整数构成的数组    void print(int (*matrix)[10], int rowSize)      { /*  ...  */}    //  再次强调, *matrix两端的括号必不可少    //  int *matrix[10];        10个指针构成的数组    //  int (*matrix)[10];      指向含有10个整数的数组的指针    // 等价定义    void print(int matrix[][10], int rowSize) { /*  ...  */}    // matrix 的声明看起来是一个二维数组,实际上形参是指向含有10个整数的数组的指针

    15. main:处理命令行选项:

    int main(int argc, char *argv[])    // 第二个形参argv是一个一维数组,它的元素是指向C风格字符串的指针    // 等价定义    int main(int argc, char **argv)    { ... }

    16. C++新标准提供了两种主要方法:如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型。和vector不一样的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。

    17. return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。

      返回一个值的方式和初始化一个变量或形参的方式完全一样:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果

    18. 不要返回局部对象的引用或指针

    // 严重错误:这个函数试图返回局部对象的引用    const string &manip()    {        string ret;        //  以某种方式改变一下ret        if (!ret.empty())            return ret;            //  错误:返回局部对象的引用        else            return "Empty";   //  错误:"Empty"是一个局部临时量    }    // 上面的两条return语句都将返回未定义的值,也就是,试图使用manip函数的返回值将    //  发生未定义的行为。对于第一条return语句来说,显然它返回的是局部对象的引用。在    // 第二条return语句中,字符串字面值转换成一个局部临时string对象,对于manip来说    // 该对象和ret一样都是局部的。当函数结束时临时对象占用的空间也就随之释放掉了,所    // 以两条return语句都指向了不再可用的内存空间。

    19. 引用返回左值。调用一个返回引用的函数得到左值,其他返回类型得到右值。

    // 我们能为返回类型是非常量引用的函数    char &get_val(string &str, string::size_type ix)    {        return str[ix];    }    int main()    {        string s("a value");        cout << s << endl;           // 输出a value        get_val(s, 0) = 'A';            // 将s[0]的值改为A        cout << s << endl;        return 0;    }    // 如果返回类型是常量引用,我们不能给调用的结果赋值

    20. C++新标准规定,函数可以返回花括号包围的值得列表,类似于其他返回结果,此处的列表也用来表示返回的临时量进行初始化。

    vector
process()    {      // ...      // expected 和 actual是string对象      if (expected.empty())      return {};      else if (expected == actual)       return { "functionX", "okay"};      else return { "functionX", expected, actual };    }     // 如果函数返回的是类类型,由类本身定义初始化值如何定义

    21. 在c++11新标准中可以使用尾置返回类型。

    // func接受一个int类型的实参,返回一个指针,该指针指向含有10个整数的数组    auto func(int i) -> int (*)[10];

    22. 使用decltype

      注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想返回指针还必须在函数声明时加一个*符号。

    int odd[] = {
1, 3, 5, 7, 9};    int even[] = {
0, 2, 4, 6, 8};    // 返回一个指针,该指针指向含有5个整数的数组    decltype(odd) *arrPtr(int i)    {      return (i % 2) ? &odd : &even;    }

    23. 函数匹配(function matching)是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫做函数确定(overload resolution)

    24. 一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值

    25. 当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用的默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面。

    26. 内联函数可以避免函数调用的开销。将函数指定为内联函数,通常就是将它们在每个调用点上"内联地"展开。另外,内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

    27. 把内联函数和constexpr函数放在头文件内,毕竟,编译器要想展开函数仅有函数的声明是不够的,还需要函数的定义。

    28. 调试帮助:assert预处理宏。所谓预处理宏其实就是一个预处理变量。 assert的行为依赖于一个名为NDEBUG的预处理变量的状态。我们可以使用一个#define语句定义NDEBUG,从而关闭调试状态。

  重点知识点总结:

    函数重载:

      如果同一作用域的几个函数名字相同但形参列表不同,我们称之为重载(overloaded)函数

      一. 重载和const形参:顶层const不影响传入函数对象。一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来:

    Record lookup(Phone);    Record lookup(const Phone);       //  重复声明了Record lookup(Phone)    Record lookup(Phone*);    Record lookup(Phone* const);     // 重复声明了Record lookup(Phone*)

    另一方面,如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的:

    // 对于接受引用或指针的函数来说,对象是常量还是非常量对应的形参不同    // 定义了4个独立的重载函数    Record lookup(Account&);            // 函数作用于Account的引用    Record lookup(const Account&);   // 新函数,作用于常量引用    Record lookup(Account*);          // 新函数,作用于指向Account的指针    Record lookup(const Account*); // 新函数,作用于指向常量的指针

    分析:上面的例子中,编译器可以通过实参是否是常量来推断应该调用哪个函数。因为const不能转换成其他类型,所以我们只能把const对象(或指向const的指针)传递给cosnt形参。相反的,因为非常量可以转换成const,所以上面的四个函数都能作用于非常量或者指向非常量的指针。不过,当我们传递非常量对象或者非常量对象的指针时,编译器会优先选择非常量版本的函数

      二. const_cast和重载

      const_cast 在重载函数的情景中最有用。例如:

    // 比较两个string对象的长度,返回较短的那个引用    const string &shorterString(const string &s1, const string &s2)    {        return s1.size() <= s2.size() ? s1 : s2;    }    // 上面的返回结果仍然是const stirng的引用,因此我们需要一种新的shorterString函数    // ,当它的实参不是常量时,得到的结果是一个普通的引用,使用const_cast可以做到这一点    string &shorterString(string &s1, string &s2)    {        auto &r = shorterString(const_cast
(s1),      const_cast
(s2));     return const_cast
(r);    }    // 这个引用事实上绑定在了某个初始的非常量的实参上,所以,我们可以再将其转换回一个    // 普通的stirng&,这显然是安全的

       三. constexpr函数

          constexpr函数只能用于常量表达式的函数。该函数的返回值类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。

        const int new_sz()  { return 42; }        const int foo = new_sz();    // 正确:foo是一个常量表达式

          编译器把对constexpr函数的调用替换成其结果值。

      四.函数匹配(p217)(待写)

      五.函数指针(p221)(待写)

  术语

    调用运算符(call operator)、自动对象(automatic object)、局部静态对象(local static object)、

    函数原型(function prototype)、引用传递(passed by reference)、值传递(passed by value)、

    递归循环(recursion loop)、最佳匹配(best match)、预处理宏(preprocessor marco)、可行函数(viable function)、

    二义性调用(ambiguous call)。

转载于:https://www.cnblogs.com/wzhe/p/6009741.html

你可能感兴趣的文章
谈谈javascript语法里一些难点问题(一)
查看>>
【BZOJ】1082: [SCOI2005]栅栏(二分+dfs)
查看>>
通过递归组合多维数组!
查看>>
ocp 1Z0-051 23-70题解析
查看>>
关于MFLAGS与MAKEFLAGS
查看>>
NotePad++ for PHP
查看>>
ssh事务回滚,纪念这几个月困扰已久的心酸
查看>>
jQuery中的编程范式
查看>>
比较快速排序,冒泡排序,双向冒泡排序的执行效率
查看>>
还没被玩坏的robobrowser(5)——Beautiful Soup的过滤器
查看>>
Linux 精准获取进程pid--转
查看>>
Servlet、Filter、Listener总结
查看>>
[翻译] JTBorderDotAnimation
查看>>
关于Thread类中三个interrupt方法的研究与学习(转)
查看>>
mysql 加入列,改动列,删除列。
查看>>
UML的学习
查看>>
ContentProvider简单介绍
查看>>
SQL SERVER获取数据库中所有表名 XTYPE类型
查看>>
java设计模式3--单例模式(Singleton)
查看>>
C#开发微信门户及应用(27)-公众号模板消息管理
查看>>