-
形参实参
void func(int a, char c) // 形参 { } int main() { func(1, 'a'); // 实参,与形参的类型、数量相匹配 return 0; }
-
局部自动对象作用域和生命周期。不要让函数返回局部自动对象的引用或者指针,否则出了该作用域后,局部自动对象自动销毁之后,主调函数拿到的就是未定义的值。
-
局部静态变量:
- 内置基本类型的局部静态变量如果没有显示地初始化,就会被初始为 0,且只会初始化一次
- 局部静态变量会被存储到存储器的全局量区,生命周期贯穿函数调用以及之后的实际直到程序终止才会被销毁
-
虽然局部变量的生命周期超出函数的作用域范围,但是局部变量只能在其所在函数的作用域范围内使用
#include <iostream> using namespace std; int counter() { static int static_cnt = 0; int stack_i = ++static_cnt; return stack_i; } int main() { for (int i = 0; i < 5; ++i) { cout << counter() << endl; } return 0; }
- 函数声明(函数原型,)不包含函数体(返回值类型还是需要的),且形参列表可以只保留参数的类型而省略参数名称,最后需要以";"结束。函数在被调用之前一定要被声明或被定义。
// 函数的声明,必须要写在函数第一次被调用之前。 // 这部分的代码可放到头文件中,用的时候include进来就可以了(分离式编译) int func(char, int); // 只要函数在被调用前声明了,那函数的定义可以写在声明之后的任意的位置, // 如这里的func就可以在声明之后、main函数之前定义; // 如果func没有在调用前声明,则必须要在被调用前定义(相当于把main函数后面的func那段代码放在这里实现) int main() { return func('a', '1'); // 函数的调用,虽然func在main后定义,但是因为之前对func进行了声明,所以编译器知道这个函数的三要素是啥 } /* * 函数的定义的参数列表中各参数的类型、数量以及位置等需要和声明时的相匹配 */ int func(char c, int i) { // do something return 0; }
-
值传递、引用传递
- 值传递传的是变量的拷贝,而不是原始的值
- 在修改指针参数指向的内存空间存储的数据时,应使用操作符*
- 操作引用参数实际操作的是参数所引用的对象,使用引用传递可以避免拷贝,比如说如果vector对象中存储了大量的元素,而其作为参数传入时不需要进行修改,则可以传递引用,这样就避免了拷贝其中的大量元素,提高效率
- 使用引用参数可以让函数返回额外的信息
-
函数的顶层const形参会被忽略掉,重载时需注意
#include <iostream> using namespace std; /** * 传进来的是main函数中变量a和b的值的拷贝 c1, c2 * 交换的也是值的拷贝,并没有改变a和b内存空间中的数据, * 所以此函数并不能交换a和b的值 */ void swap1(char c1, char c2) { char tmp = c1; c1 = c2; c2 = tmp; } /** * 传进来的是main函数中变量a和b的内存地址的拷贝 cp1, cp2 * 交换的也是内存地址的拷贝,并没有改变a和b内存空间中的数据, * 所以此函数并不能交换a和b的值 */ void swap2(char *cp1, char *cp2) { cout << "in swap2: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl; char *tmp = cp1; cp1 = cp2; cp2 = tmp; cout << "out swap2: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl; } /** * 传进来的是main函数中变量a和b的内存地址的拷贝 cp1, cp2 * 然后通过操作符*来更新内存地址中的数据,所以此函数可以成功交换a和b的值 */ void swap3(char *cp1, char *cp2) { cout << "in swap3: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl; // 取c1指向的内存空间中的数据赋值给临时变量tmp char tmp = *cp1; // 取cp2指向的内存空间中的数据赋值到cp1指向的内存空间中,覆盖原有数据 *cp1 = *cp2; // 将tmp的值赋值到cp2指向的内存空间中,覆盖原有数据 *cp2 = tmp; cout << "out swap3: *cp1=" << *cp1 << ", *cp2=" << *cp2 << endl; } /** * 传进来的是main函数中变量a和b的引用cr1, cr2 * cr1, cr2直接操作的是其引用的对象,所以此函数可以成功交换a和b的值 */ void swap4(char &cr1, char &cr2) { char tmp = cr1; cr1 = cr2; cr2 = tmp; } /** * 交换指针,过程是一样的,只是会这里需要传指针的指针进来 */ void swap_pointer(char **cp1, char **cp2) { char *tmp = *cp1; *cp1 = *cp2; *cp2 = tmp; } int main() { char a = 'A'; char b = 'B'; cout << "origin: a=" << a << ", b=" << b << endl << endl; swap1(a, b); cout << "after swap1: a=" << a << ", b=" << b << endl << endl; swap2(&a, &b); cout << "after swap2: a=" << a << ", b=" << b << endl << endl; swap3(&a, &b); cout << "after swap3: a=" << a << ", b=" << b << endl << endl; // 重置 a, b 为swap3交换之前的原值 a = 'A'; b = 'B'; swap4(a, b); cout << "after swap4: a=" << a << ", b=" << b << endl << endl; // 重置 a, b 为swap4交换之前的原值 a = 'A'; b = 'B'; char *pa = &a; char *pb = &b; printf("before swap_pointer: pa=%p, pb=%p\n", pa, pb); swap_pointer(&pa, &pb); printf("after swap_pointer: pa=%p, pb=%p\n", pa, pb); return 0; }
- 动态参数列表:void func(string msg, ...); 常见的应用场景:日志的打印
- c++11新标准允许返回花括号包围的值列表:
vector<string> test() { return {"hello", "world"}; }
- 递归,调用函数自身,main函数不能调用它自己。应用场景:虽然很多书的例子都是用的阶乘或者是斐波拉数列,但是实际工作中除了就是做算法的人,因为涉及到性能的问题,平时应该都很少用递归,我自己也只在遍历文件目录的时候用过递归。
// 伪代码 void list_dir(string dir) { files = traverse_current_dir(dir); // 遍历当前目录下的文件或者目录 for path in files: print path if path is directory: // 如果文件是目录,则调用自身遍历这个目录 list_dir(path); }
- 函数的重载:函数名与返回值一致,参数列表不一致。main函数不能重载。在调用时程序会自动根据参数列表识别被调用的函数是哪一个(重载确定)。
int get_user(int id); int get_user(string name, string mail); int main() { get_user(100); // 调用形参列表是 (int) 的 get_user("cat", "littlefawn@163.com"); // 调用形参列表是 (string, string)的 return 0; }
- 默认参数,在声明时使用的默认参数不能时局部变量
- 内联函数:编译时会在调用点上进行展开,避免函数调用的开销
#include <iostream> using namespace std; // 在函数声明的返回值之前加上关键字 inline即可 inline string short_str(const string &str1, const string &str2) { return str1.size() > str2.size() ? str1 : str2; } int main() { string s1 = "Hi"; string s2 = "world"; // short_str会被展开为:() cout << short_str(s1.size() > s2.size() ? s1 : s2) << endl; return 0; }
- constexpr函数:隐式的内联函数,除了需要遵守内联函数的约定,其返回值的类型以及所有的参数类型都必须是字面值类型,而且函数体内有且只有一条返回语句。
#include <iostream> using namespace std; constexpr int MIN_TO_SEC(int minutes) { return minutes * 60; } int main() { const int one_hour_minutes = 60; cout << one_hour_minutes << " minutes = " << MIN_TO_SEC(one_hour_minutes) << " seconds" << endl; return 0; }
- 函数指针:指向函数的指针,当所指向的函数是重载函数时,需要明确指定应选用哪个函数
#include <iostream> using namespace std; string short_str(const string &str1, const string &str2) { return str1.size() > str2.size() ? str1 : str2; } string short_str(const string &str1) { return str1; } // 参数p为函数指针 void show(const string &str1, const string &str2, string (*p)(const string &, const string &)) { cout << p(str1, str2) << endl; } // 返回指向函数的指针 using SSP = string (*)(const string &); SSP get_function() { return &short_str; } int main() { string (*ssp)(const string &, const string &); ssp = &short_str; // 如果只有一个short_str时,只需要直接赋值即可 cout << ssp("Hi", "world") << endl; string (*ssp2)(const string &) = &short_str; // 存在重载函数时,上下文需明确指定应选用哪个函数 cout << ssp2("Hi") << endl; show("my", "god", &short_str); cout << get_function()("good") << endl; return 0; }
C++primer第五版第六章学习笔记
最新推荐文章于 2022-03-04 10:33:52 发布