C++学习笔记——函数

函数基础

  1. 函数定义:返回类型、函数名、0个或多个形参组成的列表、函数体。
    • 形参以逗号隔开。
    • 形参列表位于一对圆括号内。
  2. 函数执行:调用运算符。
    • 调用运算符作用于函数或指向函数的指针。
    • 圆括号内为一个用逗号隔开的实参列表,用于对形参进行初始化。
    • 调用表达式的类型即为函数的返回类型。
  3. 函数调用过程中,完成两项工作:
    • 用实参初始化对应的形参。
    • 将程序控制权转移给被调用函数。
  4. 形参与实参:
    • 实参的求值顺序未被规定。
    • 初始化过程中,实参应与形参类型关联。
  5. 函数的形参列表:
    • 形参列表可为空。
    • 任意两个形参不可同名,且不能与函数最外层作用域中的局部变量同名。
    • 形参可以不命名,用来表示该形参在函数中不被使用,但此操作不影响实参个数。
  6. 函数的返回值:
    • 可为void。
    • 不可以是数组或函数,但可以是指向数组和指向函数的指针。
  7. 局部静态对象:在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止时被销毁。
    • 在类型声明前使用static关键字。
    • 局部静态变量没有显式的初始值时,初始化为0。
  8. 函数声明:可以声明多次,但只能定义一次。
    • 函数声明应该在头文件中,且源文件应包含头文件。

参数传递

  1. 调用类型:根据形参是否为引用分为引用传递(传引用调用)和值传递(传值调用)。
  2. 参数类型:
    • 传值参数:初始值被拷贝给变量,此时对变量的改动不会影响初始值。
    • 指针形参:可以通过指针间接访问其所指的对象。
    • 传引用参数:形参与实参绑定,函数可以通过改变形参来间接改变实参。
    • 使用引用,避免拷贝,更加高效。(无需修改参数时,使用常量引用)
    • 使用引用参数可以返回额外信息。
  3. const类型的形参与实参:实参初始化形参时会忽略顶层const限制。
//需要注意:
void func(const int i);  //func能够读取i,但不能向i写值。
void func(int i);  //由于顶层const被忽略,此处属于重复定义。
  • 尽量将函数不会改变的形参定义为常量引用。
  1. 数组形参:为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
  2. 为函数提供数组尺寸信息的方法:
    • 使用标记指定数组长度:C风格字符串中的结束符。
    • 使用标准库规范:传递指向数组首元素和尾后元素的指针(begin、end函数)。
    • 显示传递一个表示数组大小的形参:size_t类型。
  3. 数组引用形参:形参可以是数组的引用。
  4. 多维数组:数组第二维(以及后面所有维度)的大小都是数组类型的一部分,不能省略。
  5. 含有可变数目形参的函数:如果实参类型相同,可传递名为initializer_list的标准库类型;如果实参类型不同,则使用可变参数模板(后续介绍)。
  6. initializer_list可提供的操作:
initializer_list<T> lst;  //默认初始化,T类型元素的空列表。
initializer_list<T> lst{a, b, c...};  //lst元素数量与初始值一样多,lst的元素是对应初始值的副本,列表中元素为const。
lst2(lst);
lst2 = lst;  //拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,拷贝后,原始列表和副本共享元素。
lst.size();  //列表中的元素数量。
lst.begin();  //返回指向lst首元素的指针。
lst.end();  //返回指向lst尾后元素的指针。
//Example
void error_msg(initializer_list<string> il);
//调用形式
if(expected != actual)
  error_msg({"functionX", expected, actual});
else
  error_msg({"functionX", "Okay"});

返回类型和return语句

  1. return语句形式:
    • return ;
    • return expression ;
  2. 无返回值函数:无返回值的return语句只能用在返回类型是void的函数中。
    • 无返回值的函数不要求一定return语句,函数会隐式执行。
    • 使用有返回值的return语句时,要求expression必须是另一个返回值为void的函数,强行令void函数返回其他类型的表达式将引发编译错误。
  3. 有返回值函数:
    • 非void的函数必须返回一个值。
    • 未返回值便结束函数执行将引发未定义行为。
  4. 返回的之用于初始化调用点的一个临时量,该临时量即为函数调用的结果。
    • 不要返回局部对象的引用或指针。
    • 返回引用的函数将返回左值,但只有返回非常量引用时才可对其赋值。
  5. 函数可以返回花括号包围的值的列表。
  6. 返回值为int的main()函数可以没有return语句,编译器将隐式插入return 0;
    • main()函数返回0值表示执行成功,其他值表示执行失败。
  7. 递归:函数调用自身。
  8. 返回数组指针方法:
    • 使用类型别名。
    • Type (*function(parameter_list))[dimension]
    • 使用尾置返回类型:auto function(parameter_list) -> Type(*)[dimension]
    • 已知返回的指针将指向哪一个数组时,使用decltype。
int odd[] = {1, 3, 5, 7, 9};
int even[] = {2, 4, 6, 8, 10};
decltype(odd)  *arrPtr(int i)  //decltype并不会将数组类型转化为对应的指针,所以声明中需要加*符号。
{
  return (i % 2) ? &odd : &even;
}

函数重载

  1. 重载函数的形参数量、形参类型应有所不同,返回值类型不同不能作为重载函数。
  2. 函数重载时,形参的顶层const属性会被忽略,但底层const属性不会被忽略。
  3. const_cast在函数重载中的应用:
//比较两个string的长度,返回较短者的引用
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<string &>(s2));
  return const_cast<string &>(r);
}
  1. 调用重载函数时的可能结果:最佳匹配,无匹配,二义性调用。

特殊用途语言特性

  1. 默认实参:
    • 一旦某个形参被赋予默认值,其后出现的所有形参均应该有默认值(合理设置形参顺序)。
    • 给定作用域内,在函数的多次声明中,一个形参只能被赋予一次默认实参。
    • 局部变量不能作为默认实参。
string screen(sz, sz, char = ' ');
string screen(sz, sz, char = '*');  //False
string screen(sz = 24, sz = 80, char);  //True
  1. 内联函数:返回类型前加上inline关键字,声明为内联函数,减少调用开销。
    • 适用于规模较小、流程直接、频繁调用的函数。
    • 内联只是向编译器发送的一个请求,编译器可以忽略此请求。
  2. constexpr函数:
    • 函数的返回类型及所有形参的类型均应为字面值类型。
    • 函数体中有且只有一条return语句。
    • 编译器将constexpr函数隐式指定为内联函数。
    • 允许constexpr函数的返回值为非常量。
  3. 内联函数和constexpr函数一般放在头文件内。
  4. 调试帮助:
    • assert预处理宏:assert(expr),expr为0时,assert输出信息并终止程序执行,expr非0时,不执行操作。
    • NDEBUG预处理变量:如果定义了NDEBUG,则assert不执行任何操作。
    • _ _func_ _:当前调试的函数名字。
    • _ _FILE_ _:存放文件名的字符串字面值。
    • _ _LINE_ _:存放当前行号的整型字面值。
    • _ _TIME_ _:存放文件编译时间的字符串字面值。
    • _ _DATE_ _:存放文件编译日期的字符串字面值。

函数匹配

//Example
void f();  //(1)
void f(int);  //(2)
void f(int, int);  //(3)
void f(double, double = 3.14);  //(4)
  1. 确定候选函数和可行函数:
    • 候选函数:重载函数集,需与被调用函数同名且其声明在调用点可见。
    • 可行函数:可被调用这组实参调用的函数,要求形参数量与本次调用提供的实参数量相同,且类型相关。
  2. 寻找最佳匹配:
    • 实参类型与形参类型越接近,匹配越好。
    • 含有多个形参的函数最佳匹配需要满足该函数的每个实参的匹配都不劣于其它可行函数需要的匹配,且至少有一个实参的匹配优于其它可行函数提供的匹配。
  3. 调用错误:无匹配函数、二义性调用。
  4. 实参类型转换等级:
    • 精确匹配,包括:
      • 实参与形参类型相同。
      • 实参从数组、函数类型转化为对应的指针类型。
      • 向实参增删顶层const。
    • 通过const转换实现的匹配。
    • 通过类型提升实现的匹配。
    • 通过算数类型转换或指针转换实现的匹配。
    • 通过类类型转换实现的匹配。

函数指针

  1. 形式:returnType (*function)(parameter_list)
  2. 使用函数指针:
    • 当函数名作为一个值使用时,函数自动转化为指针(即取地址符可选)。
    • 可以直接通过指针调用函数,无需解引用指针。
    • 指向不同类型的函数的指针之间无转换规则。
    • 函数指针可赋值0nullptr
  3. 重载函数指针:指针类型必须与重载函数中的某一个精确匹配。
  4. 函数指针形参:可以使用类型别名或decltype简化代码。
void useBigger(const string &s1, const string &s2, bool pf(const string &, const string &));
//等价声明
void useBigger(const string &s1, const string &s2, bool (*pf)(const string &, const string &));  
//Func、Func2是函数类型
typedef bool Func(const string &, const string &);
typedef decltype(lengthCompare) Func2;
//FuncP、FuncP2是指向函数的指针
typedef bool (*FuncP)(const string &, const string &);
typedef decltype(lengthCompare) *FuncP2;
//useBigger的等价声明
void useBigger(const string &s1, const string &s2, Func);
void useBigger(const string &s1, const string &s2, Func2);
void useBigger(const string &s1, const string &s2, FuncP);
void useBigger(const string &s1, const string &s2, FuncP2);
  1. 返回指向函数的指针:
    • 直接定义。
    • 使用类型别名。
    • 尾置返回。
//直接定义
int (*f(int))(int *, int);
//类型别名
using F = int(int *, int);
using PF = int(*)(int *, int);
PF f(int);
F *f(int);
//尾置返回
auto f(int) -> int (*)(int *, int);
  1. 使用auto和decltype可简化代码书写。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,498评论 1 51
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,419评论 3 44
  • 原文地址:C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
    小猪啊呜阅读 4,581评论 1 19
  • 随着30岁生日的悄悄过去,猛然发现自己已到而立之年,有些许的迷茫,悲伤。这个年纪正是大家口中所说的事业成功的时间,...
    一生最美的美好阅读 302评论 1 0