6.1 函数基础

  • 函数包括:返回类型、函数名字、形参、函数体。
  • 通过 “调用运算符”(一对圆括号)来执行函数,它作用于一个表达式,该表达式是函数或者指向函数的指针。
  • 函数调用完成两项工作:用实参初始化形参;将控制权交给被调函数。
  • return 语句完成两项工作:返回 return 语句的值(用于初始化调用表达式的结果);将控制权交给主调函数。
  • 形参列表的形参用逗号隔开,每个形参都是含有一个声明符的声明。
  • 函数的返回值不能是数组类型或者函数类型,但可以是指向数组或者函数的指针。
  • 形参是一种自动对象,函数开始时为形参申请存储空间,函数结束时形参被销毁。
  • 局部静态对象,(定义成 static 类型)在程序的执行路径第一次经过对象语句时初始化,直到程序终止时销毁。
int count_calls() {
static int cnt = 0;
return ++cnt;
}
int main() {
for (int i = 0; i < 10; ++i)
cout << count_calls() << endl;
return 0;
}
  • 函数只能定义一次,可以声明多次。特例,如果一个函数永远不会被用到,可以只有声明没有定义。函数声明也叫函数原型。
  • 函数三要素:返回类型、函数名、形参类型。
  • 建议变量和函数在头文件中声明,在源文件中定义,确保同一函数的所有声明保持一致。

6.2 参数传递

  • 形参是引用类型:绑定到对应的实参上;否则,将实参的值拷贝后赋给形参。
  • 指针形参:可以通过指针修改它所指的对象的值。
void reset(int *p) {
*p = 0;
p = 0;
}
int main() {
int i = 42;
reset(&i);
cout << i <<endl; // 0
}
  • 通过使用引用形参,改变实参的值,使用引用避免拷贝。
  • 可以使用引用形参来返回多个结果。
  • 当用实参初始化形参时会忽略(形参的)顶层 const,当形参有顶层 const 时,传给它常量对象或者非常量对象都是可以的。
void func(const int i)
void func(int i) // 两个函数参数完全一样
  • 数组形参
// 三种等价,形参都是 const *int 类型
void print(const *int);
void print(const int[]);
void print(const int[10]);

6.3 返回类型和 return 语句

  • 如果函数返回引用,该引用仅是它所引对象的一个别名。
  • 不要返回局部对象的引用或指针,函数终止时将指向不再有效的内存区域。
  • 调用一个 “返回引用的函数” 得到左值,其他返回类型得到右值,可以像使用其他左值那样来使用 “返回引用的函数” 的调用。
  • 函数可以返回花括号包围的值的列表,如果返回的是内置类型,花括号包围的列表 最多包含一个值;如果返回的是类类型,由类本身定义初始值如何使用。
  • 函数不能返回数组,但是可以返回数组的指针或引用。
typedef int arrT[10]; // 含有 10 个整数的数组
using arrT = int[10]; // 等价声明
arrT* func(int i); // 返回指向 “含 10 个整数的数组” 的指针
  • 返回数组指针的函数形式如下
Type (*function(parameter_list))[dimention]
eg. int (*func(int i))[10]
  • 使用尾置返回类型
auto func(int i)->int(*)[10];
  • 使用 decltype 声明返回类型
int even[] = {0,2,4,6,8};
int odd[] = {1,3,5,7,9};
decltype(odd) *arrPtr(int i) {
return i % 2 ? &odd : &even;
}

6.4 函数重载

  • 重载函数:函数名字相同但形参列表不同。
  • 不允许两个函数除了返回类型之外其他都相同。
  • 顶层 const 不影响传入的函数对象,底层 const 影响。
// 顶层 const
Record lookup(Phone);
Record lookup(const Phone); // 重复定义
// 底层 const
Record lookup(Accond*);
Record lookup(const Accond*); // 新函数
  • const_cast 和重载
const string &shorterString(const string &s1, const string &s2) {
return s1.size() < s2.size() ? s1 : s2;
} string &shorterString(string s1, string s2) {
auto r = shorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));
return const_cast<string&>(r);
}
  • 调用重载函数的三种可能

    找到最佳匹配函数,生成调用该函数的代码;

    找不到任何匹配函数,发出无匹配错误;

    多于一个函数可以匹配,但每一个都不是最佳匹配,发生 “二义性” 调用错误。

6.5 特殊用途语言特性

  • 用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时。
int a = 0, b = 1, c =2;
void f1(int a = a, int b = b, int c= c) {
cout << a << b << c << endl;
}
void f2() {
int a = 4; b = 5;
f1(); // 隐藏外层的 a,局部变量与传给 f1 的默认实参无关
}
int main() {
f1(); // 012
f2(); // 052
return 0;
}
  • 内联函数(inline),在每个调用点展开,避免函数调用的开销。
  • contexpr 函数,函数的返回类型以及所有形参的类型都是字面值类型,函数体只能有一条 return 语句。
  • 允许 contexpr 函数返回值不是常数。
constexpr int foo(int cnt) {return 2 * cnt;}
foo(2); // 正确
int i = 2; foo(i); // 错误,i 不是常量表达式
  • 内联函数和 contexpr 函数通常放在头文件中。
  • assert 预处理宏,assert(expr)。

6.6 函数匹配

  • 确定候选函数
  • 与被调用的函数同名;
  • 其声明在调用点可用。
  • 确定可行函数
  • 形参数量与实参数量相等;
  • 每个实参类型与对应的形参类型相同,或者能够转换成形参类型;
  • 寻找最佳匹配(如果有的话)

    寻找一个函数
  • 该函数每个实参匹配不劣于其他函数的匹配;
  • 至少一个实参的匹配优于其他函数的匹配;

    没有的话,二义性错误。
  • 匹配等级

  • 所有算术类型的转换级别都一样,不存在哪个转换级别更高。
void manip(long);
void manip(float);
manip(3.14); // 二义性错误
  • const 实参:编译器通过实参是否是常量决定选择哪个函数。
Record lookup(Account&);
Record lookup(const Account&);
const Account a;
Account b;
lookup(a); // 选择 Record lookup(const Account&)
lookup(b); // 选择 Record lookup(Account&)

6.7 函数指针

  • 函数类型由它的返回类型和形参类型决定。
  • 声明:用指针替换函数名。
bool (*p) (const string&, const string&); // 未初始化
bool *p (const string&, const string&); // p 是一个返回值为 bool 指针的函数
  • 当我们把函数名作为一个值使用时,该函数自动转换为指针,可以直接使用指针调用该函数,无需解引用指针。
foo2 = foo1; foo2 = &foo1; // 等价赋值
foo2(); (*foo2)(); foo1(); // 等价调用
  • 重载函数指针:编译器通过指针类型决定选用哪个函数,指针类型必须与重载函数中的某一个精确匹配。
  • 函数指针形参
// 等价声明
void foo1(string s1, string s2, bool foo2(int a, int b));
void foo1(string s1, string s2, bool (*foo2)(int a, int b));
// 调用
foo1(s1, s2, foo2);

C++ Primer 5 CH6 函数的更多相关文章

  1. C++ primer ch6 函数基础-1

    1.形参和实参:编译器并没有规定实参的求值顺序. 类似下面的代码,其行为是未定义的: ; printf("%d %d\n",++i,++i); 2.变量的初始化: 如果内置类型的变 ...

  2. c++ primer,友元函数上的一个例子(By Sybase)

    本文试图解释c++ primer Screen 和 Window_Mgr的例子,为什么将两个类放在两个文件中无法编译? 将两个类写在同一个文件中,通过三个例子解释问题: 第一种写法问题: 编译到Scr ...

  3. 学习C++.Primer.Plus 8 函数探幽

    1. 内联函数 普通函数调用: 存储调用指令的地址->将函数参数复制到堆栈->跳到函数地址执行代码(返回值放到寄存器)->跳回调用指令处 2.  当代码执行时间很短,且会被大量调用的 ...

  4. 学习C++.Primer.Plus 7 函数

    C++的返回值类型不能是数组 函数原型中的变量名相当于点位符,因此不要求提供变量名. void cheers(int); C++中不指定参数列表时就使用活力号: void saybye(...); 通 ...

  5. C++ Primer 有感(函数)

    1.函数应该在头文件中声明,并在源文件中定义.(定义函数的源文件应包含声明该函数的头文件)将提供函数声明的头文件包含在定义该函数的源文件中,可使编译器能检查该函数的定义和声明是否一致. 2.既可以在函 ...

  6. 【C++ Primer | 06】 函数

    contexpr函数 const用于运行期常量,constexpr用于编译期常量 • [test1.cpp] #include <iostream> using namespace std ...

  7. 【c++ primer, 5e】函数声明 & 分离式编译

    p186~p188: 函数声明1.函数只能定义一次,但是可以声明多次. 2.函数的接口:返回类型 + 函数名 + 形参类型 3.为什么要在头文件中进行函数声明???在源文件中定义?暂时理解到,这么做可 ...

  8. 【c++ primer, 5e】函数指针

    简单的示例: #include <iostream> using namespace std; int sum(int x, int y) { return x + y; } int ma ...

  9. 【c++ primer, 5e】函数匹配

    练习 6.49 候选函数:与所调用的函数的名字相同的函数的集合. 可行函数:给候选函数加上参数数量.参数类型的约束所得到的函数的集合. 6.50 a 3.4可行,二义匹配 b 2.4可行,2是最佳匹配 ...

随机推荐

  1. swift 2.0 与 OC 相比较,标签栏和导航栏书写的差别

    下面是swift书写的时候的两个方法,其实这里不是教大家怎么样写的这个问题,我是想通过这两个不同语言的进行的一个比较,向大家找他们之间“想法”上的一些相同点,这样子我们学习swift的时候,就可以更加 ...

  2. TypeScript和JavaScript哪种语言更先进

    TypeScript和JavaScript哪种语言更先进 近两年来最火爆的技术栈毫无争议的是JavaScript,随着ES6的普及,不管是从前端的浏览器来看,还是后端的NodeJS场景,JavaScr ...

  3. $.each()遍历json数据

    var json = [ {"id":"1","tagName":"apple"}, {"id":& ...

  4. 配置NFS服务与tftp服务

    在VMware在安装ubuntu的图解 链接:http://pan.baidu.com/s/1jIofvYu 密码:da72 图解里已经解压安装了VMware Tools,接下来必须要安装的就是NFS ...

  5. BZOJ USACO 银组 水题集锦

    最近刷银组刷得好欢快,好像都是水题,在这里吧他们都记录一下吧(都是水题大家一定是道道都虐的把= =)几道比较神奇的题到时再列出来单独讲一下吧= =(其实我会说是BZOJ蹦了无聊再来写的么 = =) [ ...

  6. c++针对数据库,文件的操作总结(原始)

    1.将文件保存到sqlserver数据库的相关操作: Update t1 .txt’, SINGLE_BLOB ) Select convert( varchar(), data ) 注:fileTy ...

  7. 使用 Bitbucket Pipelines 持续交付托管项目

    简介 Bitbucket Pipelines 是Atlassian公司为Bitbucket Cloud产品添加的一个新功能, 它为托管在Bitbucket上的项目提供了一个良好的持续集成/交付的服务. ...

  8. ajax 做登录 实现页面免刷新

    结合之前学的知识,可以用ajax来传递数据,实现页面不用刷新,仅数据刷新,来看一下ajax是怎么来实现页面免刷新的 方的是客户端,圆的是服务器 如果没有ajax的话,客户端直接把数据传给服务器,服务器 ...

  9. Kubernetes运维生态-Heapster分析

    Heapster在Kubernetes的运维生态中如下:集群的容器的监控数据收敛汇聚层 heapster1.0版本后内部分为event和metric两个进程,可制作为两个docker镜像部署为两个独立 ...

  10. 如何用好消息推送(JPush)为app拉新、留存、促活

    如何用好消息推送(JPush)为app拉新.留存.促活 作为移动端APP产品运营最重要的运营手段,消息推送(JPush)被越来越多的APP厂商所重视,在信息泛滥的移动互联网时代,手机APP应用安装得越 ...