C++学习笔记(四)
2021/9/17 17:34:48
本文主要是介绍C++学习笔记(四),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
第6章 - 函数
1. 函数基础
1.1 函数的形参列表
-
形参列表中的形参通常用都好隔开,即使哥哥形参的类型一样,也必须把两个类型都写出来
int f3(int v1, v2) {/* ... */} // 错误 int f4(int v1, int v2) {/* ... */} // 正确
-
函数的返回值不能是数组,但可以是指向数组或函数的指针
1.2 局部对象
-
生命周期 (lifetime)
-
局部变量(local variable)
:形参和函数体内部定义的变量 -
自动对象(automatic object)
:只存在于快执行期间的对象,形参 是一种自动对象 -
局部静态对象(local static object)
:在程序的执行路径第一次经过对象定义语句时初始化,知道程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响// 统计自己被调用的次数 size_t count_calls() { static size_t ctr = 0; // ctr 是局部静态对象,调用结束后, return ++ctr; // ctr 仍然有效 } // 输出从1到10(包括)的数字 int main() { for (size_t i = 0; i != 10; ++i) cout << count_calls() < endl; return 0; }
1.3 函数声明(函数原型 function prototype)
-
函数的声明不包括函数体,所以可以省略形参
void print(vector<int>::const_iterator beg, vector<int>::const_iterator end);
-
函数应该在头文件中声明而在源文件中定义
-
含有函数声明的头文件应该被包含到定义函数的源文件中
1.4 分离式编译(separate compilation) 略
2. 参数传递
2.1 传引用形参
-
通过使用引用形参,函数可以改变实参的值
-
使用引用以避免拷贝
// 比较两个 string 对象的长度,由于 string 对象可能会很长,应避免直接拷贝 bool &isShorter(const string &s1, const string &s2) { return s1.size() < s2.size() }
- 如果函数无需改变引用形参的值,最好将其声明为常量引用
2.2 const
形参和实参
-
允许定义若干具有相同名字的函数,前提是不同函数的形参列表有明显区别
void fcn(const int i) {/* .. */} // fcn 能读取 i,但不能向 i 写值 void fcn(int i) {/* ... */} // 错误:重复定义了 fcn(int)
-
可以使用非常量初始化一个底层
const
对象,但是不能用底层const
对象来初始化一个非常量
2.3 数组形参
-
数组的两个性质:
- 不允许拷贝;
- 使用数组是会将其转换成指针
-
数组是以指针的形式传递给函数的,所以一开始函数并不知道数组的确切尺寸,调用者应该为此提供一些额外信息
-
使用标记指定数组长度:适用于有明显结束标记且该标记不会与普通数据混淆的情况
-
使用标准库规范:传递指向数组首元素和尾后元素的指针
void print(const int *beg, const int *end) { while(beg != end) cout << *beg++ << endl; } int j[2] = {0, 1}; print(begin(j), end(j));
-
显式传递一个表述数组大小的形参
void print(const int ia[], size_t size) { for (size_t i = 0; i != size; ++i) cout << ia[i] << endl; } int j[] = {0, 1}; print(j, end(j) - begin(j));
-
-
数组引用形参
void print (int (&arr)[10]) { for (auto elem : arr) cout << elem << endl; }
-
&arr
两端的括号必不可少f(int &arr[10]) // arr 是引用的数组 f(int (&arr)[10]) // arr 是具有10个整数的整型数组的引用
-
数组的大小是过程数组类型的一部分,限制了
print
函数的可用性
-
-
多维数组
void print(int (*matrix)[10], int rowSize) {/* ... */} // int (*matrix)[10]; 指向含有10个整数的数组的指针
- 多维数组的首元素本身就是一个数组,指针就是一个指向数组的指针;
- 多维数组第二位(以及后面所有维度)的大小都是数组类型的一部分,不能省略
2.5 main
处理命令函选项
// 假设 main 函数位于可指向文件 prog 内, 向程序传递下面的选项 prog -d -o ofile data0 int main(int argc, char *argv[]) { ... } argv[0] = "prog"; // 或者 argv[0] 也可以指向一个空字符串 argv[1] = "-d"; argv[2] = "-o"; argv[3] = "ofile"; argv[4] = "data0"; argv[5] = 0;
- 第二个形参
argv
是一个数组, 它的元素是指向C风格字符串的指针; 第一个形参argc
表示数组中字符串的数量 - 使用
argv
中的实参时, 可选的实参从argv[1]
开始,argv[0]
保存程序的名字, 而非用户输入
2.6 含有可变形参的函数 略
3. 返回类型和 return
语句
3.1 有返回值的函数
-
引用返回左值
char &get_val(string &str, string::size_type ix) { return str[ix]; } int main() { string s("a value"); cout << s << endl; get_val(s, 0) = 'A'; // 将 s[0] 的值改为 A cout << s << endl; return 0; }
- 如果函数返回类型是常量引用, 则不能给调用的结果赋值
-
函数可以返回花括号包围的值的列表
-
递归 (recursive function): 函数调用了自身
// 计算 val 的阶乘 int factorial(int val) { if (val > 1) return factorial(val - 1) * val; return 1; }
main
函数不能调用自己
3.2 返回数组指针
-
使用类型别名
typedef int arrT[10]; // arrT 是一个类型别名, 表示的类型是含有10个整数的数组 using arrT = int[10]; // 等价于上一条语句 // 声明 func 函数, 该函数返回一个指向含有10个整数的数组的指针 arrT* func(int i);
-
不使用类型别名
int arr[10]; // arr 是一个含有10个整数的数组 int *p1[10]; // p1 是一个含有10个指针的数组, 指针指向整数 int (*p2)[10]; // p2 是一个指针,指向含有10个整数的数组 // 和上面的声明一样, 定义返回数组指针的函数时, 数组的维度必须跟在函数名字之后, 例如 int (*func(int i))[10];
func(int i)
表示调用func
函数时需要一个int
类型的实参(*func(int i))
表示可以对函数调用的结果执行解引用操作(*func(int i))[10]
表示解引用得到的是一个大小是10的数组int (*func(int i))[10]
表示数组中的元素是int
类型
-
使用尾置返回类型
// func 接受一个 int 类型的参数, 返回指向含有10个整数的数组的指针 auto func(int i) -> int(*) [10]
-
使用
decltype
int odd[] = {1, 3, 5, 7, 9}; int even[] = {0, 2, 4, 6, 8}; // 返回一个指针, 指向含有5个整数的数组 decltype(odd) *arrPtr(int i) { // 对函数调用的结果解引用得到的是 odd 类型 // 的数组 return (i % 2) ? &odd : &even; // 返回一个指向数组的指针 }
4. 函数重载
-
重载函数(overloaded function)
同一作用域内的几个函数名字相同但形参列表不同 -
不允许两个函数除了返回类型外其他所有的要素都相同
-
顶层
const
不影响传入函数的对象: 一个拥有顶层const
的形参无法和另一个没有顶层const
的形参区分开来 -
如果函数的形参是某种类型的指针或引用, 则通过区分其指向的对象是否为常量可以实现函数重载, 此时的
const
是底层的 -
const_cast
和重载string::size_type reset(const string &s) { auto ret = s.size(); for (decltype(ret) i = 0; i != s.size(); ++i) { if (s[i] == '.') ret = i; } return ret; } // 重载函数 shorterString, 返回值不是 从const string &, 而是 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<const string&>(s2)); return const_cast<string&>(r); }
-
如果在内层作用域中声明名字, 它将隐藏外层作用域中声明的同名实体
5. 特殊用途语言特性
5.1 默认实参 default argument
typedef string::size_type sz; string screen(sz ht = 24, sz = wid = 80, char backgrnd = ' ');
- 一旦某个形参被赋予了默认值, 它后面的所有形参都必须有默认值
- 在给定的作用域中一个形参只能被赋予一次默认实参,
- 通常应该改在函数声明中指定默认实参, 并将该声明放在合适的头文件中
- 局部变量不能作为默认实参
5.2 内联函数和 constexpr
函数
-
内联 (inline) 函数可避免函数调用的开销, 在函数的返回类型前面加上关键字
inline
就可以将函数声明成内联函数inline const string & shortString(const string &s1, const string &s2) { return s1.size() <= s2.size() ? s1 : s2; }
- 内联说明知识像编译器发出的一个请求, 编译器可以选择忽略这个请求
- 一般来说, 内敛机制用于优化规模较小, 流程直接, 调用频繁的函数
-
constexpr
函数: 能用于常量表达式的函数- 函数的返回类型及所有形参的类型都得是字面值类型
- 函数体中必须有且只有一条
return
语句 - 为了能在编译过程中随时展开,
constexpr
函数被隐式地定义为内联函数 - 允许
constexpr
函数的返回值并非一个常量
-
调试帮助
-
assert
预处理宏assert (expr); // 当表达式 expr 为假, 输出信息并终止程序的执行 // 当表达式 expr 为真, 什么都不做
-
NDEBUG
预处理变量-
如果定义了
NDEBUG
, 则assert
什么都不做, 默认状态下没有定义NDEBUG
// 在main.c 文件的一开始写 #define NDEBUG $ CC -D NDEBUG mian.C # use /D with the Microsoft compiler
-
如果未定义
NDEBUG
, 执行#ifndef
和#endif
之间的代码; 如果定义了NDEBUG
, 这些代码将被忽略掉
-
-
调试中可能用到的名字
名字 作用 类型 定义者 __func__
存放函数名字 const char
的一个静态数组编译器 __FILE__
存放文件名 字符串字面值 预处理器 __LINE__
存放当前行号 整型字面值 预处理器 __TIME__
存放文件编译时间 字符串字面值 预处理器 __DATA__
存放文件编译日期 字符串字面值 预处理器
-
6. 函数指针
-
函数指针指向的函数而非对象, 指向某种特定类型, 函数的类型由它的返回类型和形参类型共同决定, 与函数名无关
// 比较两个 string 对象的长度 bool lengthCompare(const string &, const string &); // pf 指向一个函数, 该函数的参数是两个 const string 的引用, 返回值是 bool 类型 bool (*pf)(const string &, const string &); // 未初始化
-
*pf
代表pf
是一个指针 -
后面的形参列表表示
pf
指向的是函数 -
左侧的
bool
表示函数的返回值是bool
类型 -
*pf
两侧的括号不能省略, 不写括号时// 声明一个名为 pf 的函数, 其返回值类型为 bool* bool *pf(const string &, const string &);
-
-
使用函数指针
-
把函数名作为一个值使用时, 该函数自动得转换成指针
pf = lengthCompare; // pf 指向名为 lengthCompare 的函数 pf = &lengthCompare; // 等价于上一条语句
-
可以直接使用指向函数的指针调用该函数, 无须提前解引用指针
// 以下三条语句等价 bool b1 = pf("hello", "goodbye"); bool b2 = (*pf)("hello", "goodbye"); bool b3 = lengthCompare("hello", "goodbye");
-
typedef
// 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;
-
using
using F = int(int*, int); // F 是函数类型, 不是指针 using PF = int(*)(int *, int); // PF 是指针类型
-
这篇关于C++学习笔记(四)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-11国产医疗级心电ECG采集处理模块
- 2025-01-10Rakuten 乐天积分系统从 Cassandra 到 TiDB 的选型与实战
- 2025-01-09CMS内容管理系统是什么?如何选择适合你的平台?
- 2025-01-08CCPM如何缩短项目周期并降低风险?
- 2025-01-08Omnivore 替代品 Readeck 安装与使用教程
- 2025-01-07Cursor 收费太贵?3分钟教你接入超低价 DeepSeek-V3,代码质量逼近 Claude 3.5
- 2025-01-06PingCAP 连续两年入选 Gartner 云数据库管理系统魔力象限“荣誉提及”
- 2025-01-05Easysearch 可搜索快照功能,看这篇就够了
- 2025-01-04BOT+EPC模式在基础设施项目中的应用与优势
- 2025-01-03用LangChain构建会检索和搜索的智能聊天机器人指南