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

C++ Primer 第五版 第188页

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

bool isShorter(const string &s1,const string &s2)
{
        return s1.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(s1),
                           const_cast(s2));
 return const_cast(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,表示其不指向任何一个函数
函数指针最后关于声明和返回的部分先跳过

你可能感兴趣的:(重读C++Primer学习笔记-函数篇)