1. 函数重载
1.1 变量的作用域和生命周期
作用域
能使用某个变量的所有语句叫做这个变量的作用域。
C语言变量的作用域分为:
- 代码块作用域(代码块是{}之间的一段代码)
- 函数作用域
- 文件作用域
生命周期
变量的生命周期(Lifecycle)指的是变量从创建到销毁的过程。这个过程包括了变量的初始化、使用和最终的销毁。在C++中,变量的生命周期主要受到其作用域的影响。
1.1.1 局部变量
一般情况下代码块{}内部定义的变量都是局部变量,它有如下特点:
- 在一个函数内定义,只在函数范围内有效
- 在复合语句中定义,只在复合语句中有效
- 随着函数调用的结束或复合语句的结束局部变量的声明周期也结束
- 如果没有赋初值,内容为随机。
cpp
#include <iostream>
void test()
{
int b = 10;
}
int main(void)
{
//b = 100; //err; 在main作用域中没有b
if (1)
{
//在复合语句中定义,只在复合语句中有效
int a = 10;
std::cout << "a = " << a << std::endl;
}
//a = 10; //error 离开if()的复合语句,a已经不存在
return 0;
}1.1.2 静态局部变量
- static局部变量的作用域也是在定义的函数内有效
- static局部变量的生命周期和程序运行周期一样,同时static局部变量的值只初始化一次,但可以赋值多次
- static局部变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符串变量赋空字符
cpp
#include <iostream>
void fun1()
{
int i = 0;
i++;
std::cout << "i = " << i << std::endl;
}
void fun2()
{
// 静态局部变量,没有赋值,系统赋值为0,而且只会初始化一次
static int a;
a++;
std::cout << "a = " << a << std::endl;
}
int main(void)
{
fun1(); //i = 1
fun1(); //i = 1
fun2(); //a = 1
fun2(); //a = 2
//std::cout << "a = " << a << std::endl; //error 静态局部变量只在函数内有效
return 0;
}1.1.3 全局变量
- 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明。
- 全局变量的生命周期和程序运行周期一样。
- 不同文件的全局变量不可重名。
- 全局变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值0,字符串变量赋空字符。
cpp
#include <iostream>
int a; //全局变量
char c;
int main()
{
std::cout << "a = " << a << std::endl; //0
std::cout << "c = " << c << std::endl; //空字符
return 0;
}
//在其他文件中使用extern int a;即可
//全局变量和局部变量允许重名,当重名时局部变量优先
#include<iostream>
int a = 1; //全局变量
int main()
{
int a = 2; //局部变量
std::cout << "a = " << a << std::endl; //2
return 0;
}1.1.4 静态全局变量
- 在函数外定义,作用范围被限制在所定义的文件中。
- 不同文件静态全局变量可以重名,但作用域不冲突。
- 静态全局变量的生命周期和程序运行周期一样,同时静态全局变量的值只初始化一次。
- 静态全局变量若未赋初值,则由系统自动赋值:数值型变量自动赋初值0,字符型变量赋空字符。
cpp
#include <stdio.h>
static int a = 1; //静态全局变量,只能在本文件中使用
int main(void)
{
return 0;
}1.1.5 extern全局变量声明
声明一个变量,这个变量在别的文件中已经定义了,这里只是声明,而不是定义。extern int a;
作用域和生命周期总结表
| 类型 | 作用域 | 生命周期 |
|---|---|---|
| 局部变量 | 一对{}内 | 一对{}内 |
| 静态局部变量 | 一对{}内 | 整个程序 |
| extern变量 | 整个程序 | 整个程序 |
| 全局变量 | 当前文件 | 整个程序 |
| 静态全局变量 | 整个程序 | 整个程序 |
1.2 名字空间(命名空间)
(C++特性)std::是"标准名字空间(namespace)",C++标准库中的所有函数、变量、类型都是在std空间中。
在C++中,命名空间是一个用于封装标识符的容器。通过将相关的标识符放置在命名空间中,可以将它们从全局命名空间中隔离开来,避免不同部分的代码之间的命名冲突。
名字空间的作用
- 避免名字冲突
- 划分逻辑单元
定义名字空间
cpp
namespace 名字空间
{
名字空间成员(n个); //成员可以是全局变量、函数、名字空间
}cpp
#include <iostream>
namespace ns1
{
int num; //全局变量
void fun()
{
std::cout << "这是ns1的fun函数" << std::endl;
}
namespace ns2
{
void fun()
{
std::cout << "这是ns2的fun函数" << std::endl;
}
}
}
int main()
{
return 0;
}名字空间成员的使用
通过作用域限定操作符"::"
cppstd::cout << "ns1::num = " << ns1::num << std::endl; ns1::fun(); ns1::ns2::fun();名字空间指令
cppusing namespace 名字空间名;在该条指令以后的代码中,指定名字空间中的成员都可以直接访问,省略"空间名::"。
常用场景:using namespace std;
cpp
#include <iostream>
using namespace std;
namespace ns1
{
int num;
void fun()
{
cout << "这是ns1的fun函数" << endl;
}
namespace ns2
{
void fun()
{
cout << "这是ns2的fun函数" << endl;
}
}
}
int main()
{
using namespace ns1;
cout << "num = " << num << endl;
fun();
ns2::fun();
return 0;
}- 名字空间声明cpp将名字空间中特定的一个成员引入当前作用域,可以直接访问。
using 名字空间名::名字空间成员;
cpp
using ns1::num;
cout << "num = " << num << endl;- 无名名字空间
不属于任何名字空间的标识符会被自动放入无名名字空间。无名空间成员可直接访问,但当与局部变量同名时局部优先。此时可用::成员名访问无名空间成员。
cpp
#include<iostream>
using namespace std;
namespace ns1 { int num = 100; }
namespace ns2 { int num = 200; }
int num = 300; //无名名字空间
int main()
{
cout << num << endl; //300
using ns1::num;
cout << num << endl; //100
cout << ns2::num << endl; //200
cout << ::num << endl; //300
return 0;
}1.3 函数重载
(C++特性)
在相同作用域定义同名函数,但参数列表不同,构成重载关系。
cpp
#include<iostream>
using namespace std;
void fun(int a) { cout << "fun(int)" << endl; }
void fun(int a, int b) { cout << "fun(int,int)" << endl; }
void fun(int a, float b) { cout << "fun(int,float)" << endl; }
int main()
{
fun(1);
fun(1, 2);
fun(1, 2.1f);
return 0;
}函数重载与返回值无关,以下不能构成重载:
cpp
#include<iostream>
using namespace std;
void fun(int a) { cout << "fun(int)--void" << endl; }
int fun(int a) { cout << "fun(int)--int" << endl; return a; } //error
int main()
{
fun(1);
return 0;
}函数重载匹配规则
编译器匹配规则:
完全匹配 > 常量匹配 > 升级转换 > 降级转换 > 省略号匹配
cpp
#include<iostream>
using namespace std;
void bar(int i) { cout << "bar(int)--升级转换" << endl; } //char->int
void bar(const char c) { cout << "bar(const)--常量转换" << endl; } //char->const char
void fun(char c) { cout << "fun(char)--降级转换" << endl; } //short->char
void fun(int i) { cout << "fun(int)--升级转换" << endl; } //short->int
void hum(int i, ...) { cout << "hum(...)--省略号匹配" << endl; } //省略号匹配
void hum(int i, int j) { cout << "hum(int,int)--降级转换" << endl; } //double->int
int main()
{
char c = 'A';
bar(c); //调用bar(const char)
short s = 10;
fun(s); //调用fun(int)
hum(10, 2.1); //调用hum(int,int)
return 0;
}函数重载实现原理
C++编译器通过换名(Name Mangling)将参数信息整合到新名字中解决重载。例如:void fun(int a, float b) 可能被重命名为 fun_857if
2. 随机数
2.1 time()函数
cpp
time_t time(time_t *seconds); //返回自1970-01-01起经过的秒数
#include <ctime>
int startTime = time(NULL);2.2 srand()函数
cpp
void srand(unsigned int seed); //设置随机数种子
#include <cstdlib>
srand(1);
//推荐使用时间作为种子
srand(time(NULL));2.3 rand()函数
cpp
int rand(void); //生成0~RAND_MAX之间的随机数
#include <cstdlib>
int a = rand() % 10; //0~9的随机数练习:石头剪刀布游戏
cpp
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
srand(time(NULL));
int num1, num2;
while(1)
{
num1 = rand() % 3; //2:石头 1:剪刀 0:布
cout << "2石头1剪刀0布" << endl;
cout << "input a num:";
cin >> num2;
if(num2<0 || num2>2) {
cout << "error input" << endl;
return 0;
}
if(num1 > num2) {
if(num1==2 && num2==0) cout << "You win" << endl;
else cout << "You lose" << endl;
break;
}
else if(num1 < num2) {
if(num1==0 && num2==2) cout << "You lose" << endl;
else cout << "You win" << endl;
break;
}
else {
cout << "你" << num2 << "电脑" << num1 << endl;
cout << "again" << endl;
}
}
cout << "你" << num2 << "电脑" << num1 << endl;
return 0;
}3. 递归函数
函数调用自身,包含:
- 基本情况(终止条件)
- 递归调用
cpp
#include<iostream>
using namespace std;
int add(int max)
{
if(max == 0) return 0; //终止条件
return max + add(max-1); //递归调用
}
int main()
{
int max;
cout << "input a num:";
cin >> max;
cout << "the sum is " << add(max) << endl;
return 0;
}4. lambda函数
(C++特性)
匿名函数,语法:[捕获列表](参数)mutable->返回类型{函数体};
4.1 捕获列表
[]:不捕获外部变量[=]:值方式捕获所有外部变量[&]:引用方式捕获所有外部变量[a]:值方式捕获特定变量[&a]:引用方式捕获特定变量
cpp
#include<iostream>
using namespace std;
double d = 5.0; //全局变量
int main()
{
int a = 1;
auto c = [a]() //值捕获a
{
double b = 2.0;
d = 6.0; //可修改全局变量
//a = 2; //error(无mutable)
return d / b;
};
auto d = [a](int i) mutable //mutable允许修改值捕获的副本
{
a = 10; //修改的是副本
return a + i;
};
cout << c() << endl; //3.0
cout << d(5) << endl; //15
cout << "a = " << a << endl; //1(原始值未变)
cout << "d = " << d << endl; //6.0
return 0;
}4.2 其他特性
- 参数列表:可省略空括号
[]{} - 返回类型:可自动推导,省略
->返回类型 - 函数体:可访问全局变量并修改
