【写在前面】
基础语言的复习需要在短时间内迅速完成,对C++语言有了解的人可以简单复习,本篇基础篇会慢慢更新。先更新我认为一些比较重要的规则和一些容易错误的知识点。C++语言颗粒密度很细,想要一次性全部记忆很难,我们先对基础的语言有个了解,后续可以根据这个提纲再看C++ Primer!
六 函数
函数是编程中必不可少的一个过程,在C语言时候我们已经基本上了解了函数函数指针等。本章节只会重点讲解C++特有的函数重载,函数匹配,温习函数指针,其他章节只会简单介绍掠过。以后有时间会继续慢慢补充笔记~~
6.1 函数基础
6.1.1 初识函数,什么是函数?函数的定义和实现
数据类型 函数名(数据类型 形参名,数据类型 形参名…)
函数可以是void 无返回值
也可以是有返回值的。
6.1.2. 局部对象
函数和变量都有其作用域,而我们的局部变量是有生命周期的,局部变量的在我们初始化之后由操作系统管理,在我们这个变量所在的函数执行完毕了会自动释放。
形参和函数体内定义的变量统称局部变量。
函数开始时为形参申请存储空间,函数终止形参被销毁。
在所有函数外定义的对象存在于程序的整个执行过程中。
6.1.3局部静态变量
如果要让局部变量的生命周期不局限于函数内,可以将局部变量定义成 static 类型。(static表示静态的全局的意思)
局部静态变量的生命周期:在程序的执行路径第一次经过对象定义语句时初始化,直到程序终止被销毁。
如果局部静态变量没有显式的初始值,将执行值初始化。
注意:局部静态变量是 static 型而不是 const 型,const是常量的意思不能够多次改变。但是static是静态全局的意思,和生命周期相关。const要是希望能够在其他文件使用,需要用extren
6.2 参数传递
参数传递可以分为三种
值传递:不会修改实参
指针传递:可以修改实参
引用传递:可以修改实参(C++中最常用这个!)
6.3 返回类型和return
返回类型和函数指定的数据类型类型有关系
void 可以直接return;
int 可以返回整形
string 返回字符串 return str1;
6.4 函数重载
6.4.1. 函数重载作用域
函数的重载是区分作用域的,作用域不同的函数不能混用
main函数无法重载
重载的函数通过不同类型不同数目的形参,编译器会自动匹配
6.4.2 const_cast 在重载中的应用
强制类型转换 const_cast 在重载函数中最有用。一个函数可能同时要有接受常量引用的版本也要有接受非常量引用的版本。
当要重载常量引用与非常量引用的版本时,在非常量引用的版本中可以通过 const_cast 将参数和返回值从常量引用转换为非常量引用,以实现对常量引用版本的调用。string& s;
const_cast <const string&> (s);// 将 s 转换为常量引用
const_cast <string&> (s);// 将 s 转换回非常量引用
6.5 特殊用途语言特性
>6.5.1. inline内联函数和constexpr函数
将规模较小的操作定义为函数的优点:阅读和理解函数调用更简单;使用函数可以确保行为统一;修改函数更方便;函数可以被重复利用
使用函数的缺点:调用函数更慢
原因:需要保护现场及恢复等一系列操作,可能要拷贝实参,程序要转到新位置继续执行。
使用内联函数可以避免调用函数的时间开销
在函数前用 inline 声明一下即表明是内联函数
内联函数适用于规模小、流程直接、频繁调用的函数
constexpr函数
constexpr 函数被隐式地指定为内联函数
cosntexpr 是指能用于常量表达式的函数。但是 constexpr 函数不一定返回常量表达式。
constexpr 函数的返回类型及所有的形参类型都必须是字面值类型,函数体中必须有且只有一条 return 语句。
内联函数和 constexpr 函数可以多次定义,但是多个定义必须完全一致。
应该把内联函数和constexpr函数的定义放到头文件里。
6.5.2 assert函数
assert 预处理宏
用法:assert(expr); assert 宏定义在头文件 cassert 中。
如果表达式 expr 为假,assert 输出信息并终止程序,如果表达式为真,assert 什么也不做。
assert 常用于检查“不能发生”的条件
6.6 函数匹配
6.7 函数指针
函数指针是指向函数类型的指针,一种函数指针只能指向一种特定的函数类型.
bool Compare(const string&, const string&); // 此函数的类型是 bool(const string&, const string&);
bool (*pf)(const string&, const string&); // 声明了一个指向 bool(const string&, const string&) 类型函数的指针,注意括号不能少;
pf = Compare; // 给指针赋值,指向 Compare 函数
pf = &Compare; // 这两种赋值语句完全等价
bool b1 = pf("hello","goodbye"); // 可以直接使用指针替代函数名调用函数。
bool b2 = (*pf)("hello","goodbye"); // 与上面的等价
函数指针的别名
函数指针写起来很复杂,尤其是将函数指针作为函数的返回值时,因此一般为其定义别名。定义别名时要注意区分函数类型、函数指针。
下面几个等价:定义的别名都是函数类型。
typedef bool func(const string&, const string&); // 定义了一个别名:func,但是 func 是函数类型
typedef decltype(Compare) func2; // 定义了一个别名:func2,func2 也是函数类型
using func3 = bool(const string&, const string&); // 定义了一个别名:func3,func3 也是函数类型
注意 decltype(函数名) 返回的不是指针,是函数类型,加上 * 才表示函数指针。
下面几个等价:定义的别名都是函数指针。
typedef bool (*func)(const string&, const string&); // 定义了一个别名:func, func 是函数指针
typedef decltype(Compare)* func2; // 定义了一个别名:func2,func2 也是函数指针
using func3 = bool(*)(const string&, const string&); // 定义了一个别名:func3,func3 也是函数指针
函数指针形参(本章节比较绕口 需要边看书边理解!!!!)
函数不能作形参,但是函数指针可以做形参,之后在调用时可以直接传入函数名作实参
函数名做形参也会自动的转换为指针。
bool GetBigger(const string& s1, const string& s2, bool(*comp)(const string&, const string&)); // 函数指针做形参
GetBigger(s1, s2, Compare); // 实参直接传入函数名 Compare
//返回函数指针
//不能返回一个函数,但是可以返回函数指针(注意这时函数名不会自动转换为函数指针)。
//声明一个返回函数指针的函数有几种方法,其中直接声明最麻烦,使用尾置类型和 decltype 更简单一些,但是最好使用类型别名。
//'直接声明'
bool (*f1(int))(double); // f1 是一个函数,函数的形参列表为 int,返回类型为函数指针。这个函数指针的类型为 bool (*)(double),即形参为 double,返回类型为 bool。
//'使用尾置类型'
auto f1(int) -> bool(*)(double);
//'使用 decltype'
bool func(double);
decltype(func)* f1(double);
//'使用类型别名'
using PF = bool(*)(double);
PF f1(int);