目录
1.局部静态对象(static)
在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。
注:静态对象只在第一次执行到定义语句时创建出来,之后即使函数执行结束,它的值依然保持;下一次函数调用时,不会再次创建、也不会重新赋值,而是直接在之前的值基础上继续叠加。
void callCount()
{
static int cnt = 0; // 静态对象只会创建一次
cout << "我被调用了" << ++cnt << "次!" << endl;
}
int main()
{
//cout << cnt << endl; // 错误,局部变量在作用域外不可见
callCount(); //1
callCount(); //2
callCount(); //3
cin.get();
}
2.参数传递(值传递,地址传递,引用传递)
值传递时,函数实参不变
地址传递时,函数的实参发生变化
引用传递时,函数的实参发生变化,常量引用为形参时,实参不会发生变化:
2.1值传递:
直接将一个实参的值,拷贝给形参做初始化的传参方式,就被称为“值传递”,
相当于将实参的值拷贝给形参
缺点:实际应用中,很多时候函数要操作的对象可能非常庞大,如果做值拷贝会使得效率大大降低;这时使用引用就是一个好方法。
2.2引用传递 :
常量引用作为形参,实参既可以是常量,也可以是变量
// 比较两个字符串的长度
bool isLonger(const string & str1, const string & str2)
{
return str1.size() > str2.size();
}
int main()
{
string str1 = "hello", str2 = "worlds";
const string cstr1 = "jia", cstr2 = "li";
cout << isLonger(str1, str2) << endl; 实参是变量
cout << isLonger(cstr1, cstr2) << endl; 实参既是常量
cin.get();
}
2.3地址传递
//地址传递示例
#include<iostream>
using namespace std;
void swap(int *p1,int *p2) {
int tem = *p1;
*p1 = *p2;
*p2 = tem;
}
int main() {
int a = 5, b = 10;
swap(&a, &b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
2.4数组作为实参
值传递:数组是不允许做直接拷贝的,因此不能使用值传递
地址传递:
void printArray(const int* arr, int size){
}
printArray(arr); //数组名就为其地址
引用传递:
// 使用数组引用作为形参
void printArray(const int(&arr)[5])
printArray(arr);
3.函数的默认参数
1.如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须要有默认值。
2.如果函数的声明有默认值,函数实现(定义)的时候就不能有默认参数。即声明和实现只能有一个有默认参数。
3.如果我们自己传入参数,就用我们的传入参数,如果没有,就用默认值
4.占位参数
语法: 返回值类型 函数名(数据类型){函数体} eg:void func(int){}
1.占位参数还可以有默认参数 eg:void func(int = 10){}
5.函数重载(函数名可以相同,提高复用性)
函数重载满足条件:
1.同一个作用域下
2.函数名称相同
3.函数参数 类型不同 或者 个数不同 或者 顺序不同
注意:函数的返回值不同不可以作为函数重载的条件
引用可以作为重载条件
//函数重载
#include<iostream>
using namespace std;
//形参&c为引用
void func(int &c) {
cout << "&c为引用" << endl;
}
void func(const int &c) {
cout << "const &c为引用" << endl;
}
int main() {
int a = 5, b = 10;
func(a); //传入实参a时, int &c = a
func(10); //传入实参10时,const int &c = 10
system("pause");
return 0;
}
6.函数返回值
返回值原理:
函数在调用点会创建一个“临时量”,用来保存函数调用的结果。当使用return语句返回时,就会用返回值去初始化这个临时量。 一下函数,假设str1更长,所以将用str1对一个string临时量做初始化,执行的是值拷贝。最终返回的值,是str1的一个副本。
显然做值拷贝并不高效。使用引用类型来做返回值的传递,这样就可以避免值拷贝。
string longerStr(const string& str1, const string& str2)
{
return str1.size() > str2.size() ? str1 : str2;
}
7.返回值为引用类型
使用引用类型来做返回值的传递,这样就可以避免值拷贝。
// 返回一个string常量对象的引用,不做值拷贝
const string & longerStr(const string& str1, const string& str2)
{
return str1.size() > str2.size() ? str1 : str2;
}
注意:不能返回函数内局部变量的引用,或返回指向局部对象的指针,因为局部对象,函数执行完成后就销毁了。如下
错误的情况
const string & f()
{
string str = "test";
return str;
}
8.返回值为数组:
由于数组“不能拷贝”的特点,函数也无法直接返回一个数组。同样的,我们可以使用指针或者引用来实现返回数组的目标;通常会返回一个数组指针。
int arr[5] = { 1,2,3,4,5 };
int* pa[5]; // 指针数组,pa是包含5个int指针的数组
int(*ap)[5] = &arr; // 数组指针,ap是一个指针,指向长度为5的int数组
int(*fun(int x))[5]; // 函数声明,fun返回值类型为数组指针
这里对于函数fun的声明,我们可以进行层层解析:
- fun(int x) :函数名为fun,形参为int类型的x;
- ( * fun(int x) ):函数返回的结果,可以执行解引用操作,说明是一个指针;
- ( * fun(int x) )[5]:函数返回结果解引用之后是一个长度为5的数组,说明返回类型是数组指针;
- int ( * fun(int x) )[5]:数组中元素类型为int
数组指针的定义比较繁琐,为了简化这个定义,我们可以使用关键字typedef来定义一个类型的别名:
typedef int arrayT[5]; // 类型别名,arrayT代表长度为5的int数组
arrayT* fun2(int x); // fun2的返回类型是指向arrayT的指针
9.main函数接受外界参数
#include <iostream>
using namespace std;
//argc 代表argument cout, 参数数量
//argv 代表argument vector, 参数列表
int main(int argc, char** argv)
{
cout << "argc is" << argc <<endl;
for(int i = 0; i < argc; i++){
cout << "argv[i] is "<< argv[i] <<endl;
cout << "i is" << i <<endl;
}
return 0;
}
运行时,在VScode终端输入以下指令:
/home/rui/VScode/release/main arc1 2 3
第一个参数为此文件生成的执行文件路径,此指令包括4个参数(路径也是参数)
参数列表argv为[/home/rui/VScode/release/main, arc1, 2, 3]