Skip to content

1. 函数重载

1.1 变量的作用域和生命周期

作用域
能使用某个变量的所有语句叫做这个变量的作用域。
C语言变量的作用域分为:

  • 代码块作用域(代码块是{}之间的一段代码)
  • 函数作用域
  • 文件作用域

生命周期
变量的生命周期(Lifecycle)指的是变量从创建到销毁的过程。这个过程包括了变量的初始化、使用和最终的销毁。在C++中,变量的生命周期主要受到其作用域的影响。

1.1.1 局部变量

一般情况下代码块{}内部定义的变量都是局部变量,它有如下特点:

  1. 在一个函数内定义,只在函数范围内有效
  2. 在复合语句中定义,只在复合语句中有效
  3. 随着函数调用的结束或复合语句的结束局部变量的声明周期也结束
  4. 如果没有赋初值,内容为随机。
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 静态局部变量

  1. static局部变量的作用域也是在定义的函数内有效
  2. static局部变量的生命周期和程序运行周期一样,同时static局部变量的值只初始化一次,但可以赋值多次
  3. 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 全局变量

  1. 在函数外定义,可被本文件及其它文件中的函数所共用,若其它文件中的函数调用此变量,须用extern声明。
  2. 全局变量的生命周期和程序运行周期一样。
  3. 不同文件的全局变量不可重名。
  4. 全局变量若未赋以初值,则由系统自动赋值:数值型变量自动赋初值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;
}

名字空间成员的使用

  1. 通过作用域限定操作符"::"

    cpp
    std::cout << "ns1::num = " << ns1::num << std::endl;
    ns1::fun();
    ns1::ns2::fun();
  2. 名字空间指令

    cpp
    using 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;
}
  1. 名字空间声明
    cpp
    using 名字空间名::名字空间成员;
    将名字空间中特定的一个成员引入当前作用域,可以直接访问。
cpp
using ns1::num;
cout << "num = " << num << endl;
  1. 无名名字空间
    不属于任何名字空间的标识符会被自动放入无名名字空间。无名空间成员可直接访问,但当与局部变量同名时局部优先。此时可用::成员名访问无名空间成员。
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. 递归函数

函数调用自身,包含:

  1. 基本情况(终止条件)
  2. 递归调用
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 其他特性

  • 参数列表:可省略空括号 []{}
  • 返回类型:可自动推导,省略->返回类型
  • 函数体:可访问全局变量并修改

知识如风,常伴吾身