重读C++Primer学习笔记-函数篇

C++ Primer 第五版 第188页

值参数和引用参数
当初始化一个非引用类型的变量时,初始值被拷贝给变量,对变量的改动不影响初始值
指针的行为和其它非引用类型一样 拷贝的是指针的值 因为指针可以间接访问他指向的对象 所以通过指针可以修改他所指对象的值
C语言程序员常使用指针访问函数外部对象 C++程序员则建议使用引用类型
引用形参绑定其初始化的对象,使用引用可以避免拷贝
拷贝大的类类型对象或者容器对象效率较低,甚至有的类类型(包括IO类型在内)根本不支持拷贝,自定义类型尤其会有未定义的拷贝操作
举个例子 ,我们准备比较两个string的长度,这两个对象可能非常长,那么用引用就显得特别明智,尤其是我们只是比较一下长度

bool isShorter(const string &s1,const string &s2)
{
        return s1.size()<s2.size();
}

如果无需修改 那么将其定义为常量引用更好
当形参有顶层const时,被忽略了,传给他const或者非const都是可以的

C++ Primer 第五版 第192页

尽量使用常量引用
把函数不会改变的形参定义为普通的引用,对调用者是一种误导,即函数会改变传入的值
另外,不定义为常量引用,会极大的限制函数可以接受的实参类型

string::size_type find_char(string&s, char c,
string::size_type &occurs);
//只能将find_char作用于string对象,而
find_char("hello world",'o',ctr);
//将在编译时报错

C++ Primer 第五版 第193页

尽管不能以值传递的形式传递数组,但是我们仍然可以将形参写成类似数组的形式

// 尽管形式不同,但等价
void print(const int*);
void print(const int[]);
void print(const int[10]);//这里的维度是我们希望的维度,但不一定

C++ Primer 第五版 第195页

c++ 允许将变量定义为数组的引用,那么形参也可以是数组的引用

void  print(int (&arr)[10])
{
     for (auto elem:arr)
        cout<< elem << endl;
}
//括号不能少
f(int &arr[10] ) //错误 定义为引用的数组

同时 由于数组大小是构成数组类型的一部分,只要不超过维度 函数体内就可以放心使用数组 但这一用法无形中限制了,导致我们只能将函数用于大小为10的数组
16.1.1节(第578页)将介绍如何传递任意大小数组

C++ Primer 第五版 第197页

initializer_list形参
如果函数的实参数量未知但全部实参类型已知,可以用该类型的形参
注意这也是一种模板类型
省略符形参应该仅用于C和C++通用的类型

C++ Primer 第五版 第200页

没有返回值的return语句只能用于返回类型为void的函数中,且函数不要求非得有return语句,这类函数最后会隐式执行return

C++ Primer 第五版 第201页

值是如何被返回的
返回一个值的方式和初始化一个变量或形参的方式是完全一样的:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果
不要返回局部对象的引用或指针:小建议,要确认返回值安全,我们不妨想一下引用是否在函数调用之前就已经存在
引用返回左值
C++11新标准允许函数返回花括号包围的值列。类似其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化
和其他函数不同,我们允许main函数结尾没有return直径结束,编译器可以隐式的插入一挑返回0的return语句,返回0表示执行成功

C++ Primer 第五版 第205页

返回数组指针
从语法上说 定义一个返回数组的指针或者引用的函数比较繁琐,但有些方法可以简化这个任务,例如利用别名

typedef int arrT[10];//arrT是一个类型别名,代表含有10个整数
                                //的数组
using arrT int[10];//等价声明
arrT* func(int i);//返回一个含有10个整数的数组的指针

要想在声明func时不使用类型别名,我们必须牢记数组的维度

int arr[10];//
int *p1[10];// p1是一个含有10个指针的数组
int (*p2)[10] = &arr;//p2是一个指针,指向一个含有10个整数的数组

和这些声明一样,想定义一个返回数组的函数,必须指定数组的维度,那么函数形式如下

Type (*function(param_list))[dimension]
//具体的
int (*func(int i))[10];
//(*func(int i)) 代表可以对函数返回解引用
//(*func(int i))[10]代表解引用后返回的是一个维度是10的数组

在C++11 中有一种新的简化形式,即尾置返回类型
任何函数的定义都可以使用尾置返回,但对于返回类型较复杂的函数最有效

//func接受一个int类型的实参,返回一个指针,指针指向
//一个含有10个整数的数组
auto func(int i) -> int (*)[10];
//为表示真正的返回类型在最后,我们在本该出现返回类型的
//位置放置一个auto

C++ Primer 第五版 第207页

如果同一作用域内的几个函数名字相同,形参列表不同,将其称为重载函数
main不能重载
两个函数只有返回类型不同的话,第二个函数的声明就是错的
一个拥有顶层const的形参无法同没有的相区分

Record lookup(Phone);
Record lookup (const Phone);

Record lookup(Phone *);
Record lookup(Phone* const);//上面每组两个声明无法区分

Record lookup(Account&);
Record lookup(const Account&);

Record lookup(Account*);
Record lookup(const Account*);//上述两组 底层const 可以重载

虽然重载减轻了命名的难度,但最好重载那些确实非常相似的操作

c++ Primer 第五版 第209页

const_cast和重载
我们曾经说过,const_cast在重载上下文中很有用

const string &shorterString(const string&s1,const string&s2)
{
  return s1.size() <= s2.size();
}

该函数的参数和返回类型都是const string,即使对string调用也是如此,因此可以定义一个新的函数,使得实参不是常量时,得到一个普通的 引用

string &shortString(string&s1,string&s2)
{
  auto &r = shortString(const_cast<const string&>(s1),
                           const_cast<const string&>(s2));
 return const_cast<string&>(r);
}

c++ Primer 第五版 第209页

重载函数之后 需要函数匹配 也叫做 重载确定过程
可能出现三种情况

  • 编译器找到一个和实参最佳匹配的函数并调用
  • 找不到任何一个函数匹配,报错 无匹配
  • 有多于一个函数可以匹配,但每一个都不是最佳选择,因此报错,也称为二义性调用

c++ Primer 第五版 第212页

对于函数的声明来说,通常的习惯是将其放在头文件中并声明一次,但多次声明同一个函数也是合法的
需要注意的是,每一个形参只能被赋予一次默认实参

string screen (sz,sz,char=' ');
string screen(sz,sz,char='*');//非法,char不能声明两次
string screen(sz=24,sz=24,char);//可以,sz也得到了声明

C++规定一旦某个形参被赋予了默认实参,其后面的所有形参都必须有默认实参,为了防范可能的二义性

c++ Primer 第五版 第213页

使用规模较小的操作定义成函数有很多好处,主要包括:

  • 阅读和理解函数的调用比读懂等价的式子简单
  • 使用函数可以确保行为的统一
  • 修改计算过程的话 修改函数只需要修改一次 而修改操作可能修改多次
  • 函数可以重复利用 省去重新编码的麻烦
    然而函数也存在一个潜在的问题,即比表达式要慢一些;在大多数机器上 一次函数调用涉及很多操作,因此可以使用内联函数避免函数调用的开销
    加上inline可以将其声明为内联函数
    但是内联只是编译器发出的一个请求,编译器可以选择忽略

c++Primer第五版 第214页

constexpr函数需要遵循:

  • 返回值以及形参的类型都必须是字面值类型
  • 函数有且只有一条return
    constexpr函数不一定返回常量表达式
    和其他函数不一样,,内联函数和constexpr函数的多次定义必须完全一致,因此通常定义在头文件

c++ Primer 第五版 第215页

assert预处理
assert(expr);
首先对expr求值,如果为假,则输出信息并终止程序运行,为真assert什么也不做
assert由预处理器管理而不是编译器管理,也就是说不需要使用using
assert宏常用于检查“不能发生”的条件
assert行为依赖于NDEBUG的预处理器变量,如果定义了NDEBUG,assert什么也不做

c++ Primer 第五版 第221页

使用函数指针
当我们把函数名作为值使用时,该函数自动转换为指针

pf  = lengthCompare;
pf = &lengthCompare;//等价,指向lengthCompare函数,&取址符可选
//此外,可以直接使用而无需解引用
bool b1 = pf("hello","goodby");
bool b2 = (*pf)("hello","goodby");//等价
/

指向不同函数类型的指针间不存在转换规则,但和往常一样,我们可以为函数指针赋值nullptr或者0,表示其不指向任何一个函数
函数指针最后关于声明和返回的部分先跳过

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • 因为自己记忆力减退的关系(手动狗头),到了看书要做笔记的年纪了,所以重读一些编程笔记的时候,有很多概念需要进行一下...
    居者有其屋阅读 145评论 0 0
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,216评论 0 4
  • 3. 类设计者工具 3.1 拷贝控制 五种函数拷贝构造函数拷贝赋值运算符移动构造函数移动赋值运算符析构函数拷贝和移...
    王侦阅读 1,793评论 0 1
  • 1、函数基础 典型的函数定义包括:返回类型、函数名、由0个或多个形参组成的列表以及函数体。 2、参数传递 形参初始...
    久伴必知情深阅读 483评论 0 0
  • #1.函数基础1.1 局部对象1.2 函数声明1.3 分离式编译 #2.参数传递2.1 传值参数2.2 传引用参数...
    MrDecoder阅读 588评论 0 1