C++【3】(函数重载,引用和动态空间分配)

494 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、函数重载

在C++语言中,一个程序里面,如果多个函数的功能相同,但是只要保证参数的个数或者类型不一样,这些函数的名字可以定义成同一个。

为什么C++可以实现函数重载

  • C语言中函数的地址只是由函数的名字确定
  • C++语言中函数的地址是由函数名参数共同确定的
#include <iostream>

using namespace std;

void MyAdd(int a, int b)
{
    cout << a << " + " << b << " = " << a + b << endl;
}

void MyAdd(float a, float b)
{
    cout << a << " + " << b << " = " << a + b << endl;
}

void MyAdd(long a, long b)
{
    cout << a << " + " << b << " = " << a + b << endl;
}

void MyAdd(int a, float b)
{
    cout << a << " + " << b << " = " << a + b << endl;
}

void MyAdd(int a)
{
    cout << "a = " << a << endl;
}

void test1()
{
    MyAdd(100, 20);
    MyAdd(3.14f, 8.18f);
    MyAdd(100, 5.16f);
}

void MyFun(int a = 333, int b = 666, int c = 888)
{
    cout << "a = " << a << ", ";
    cout << "b = " << b << ", ";
    cout << "c = " << c << endl;
}

void MyFun(int a, int b)
{
    cout << "a = " << a << ", ";
    cout << "b = " << b << endl;
}

void MyFun(int a)
{
    cout << "a = " << a << endl;
}

void test2()
{
    //当函数重载和函数的默认参数一起使用时,一定要注意会不会出现歧义
    //MyFun(100);
    //MyFun(100, 200);

    MyFun();
}

//注意:返回值不能作为函数重载的条件
#if 0
void MySub(int a, int b)
{
    cout << "a - b = " << a - b << endl;
}

int MySub(int a, int b)
{
    cout << "a - b = " << a - b << endl;
    
    return a - b;
}
#endif

int main()
{
    test2();

    return 0;
}

二、引用

https://note.youdao.com/yws/public/resource/d23ff061138554293b27d88e32be9553/xmlnote/465083A8FBC044BCA73424A8FA98A95E/0092E34334F4436AA63876523E29F98A/84048

2.1 引用的基本使用

#include <iostream>

using namespace std;

void test1()
{
    //定义一个变量保存常量100
    //当运行到这部分代码时,就会在内存中开辟一块空间来存放100
    //此时的a也称之为这块空间的别名
    int a = 100;
    cout << a << endl;

    //将变量a的地址保存在指针变量p中
    //保存之后这个指针变量就可以对a变量地址里面的内容进行操作
    int *p = &a;

    *p = 666;
    cout << a << endl;
}

void test2()
{
    int a = 100;
    //引用:就是对已有的空间重新再起一个别名
    //定义一个引用
    int &b = a;

    cout << "&a = " << &a << endl;
    cout << "&b = " << &b << endl;

    b = 888;
    cout << a << endl;
}

int main()
{
    test2();

    return 0;
}

2.2 引用的注意事项

1、引用==必须初始化==,他必须是对已有的空间进行去别名

2、引用只能初始化一次意味着初始化之后,引用就不能再作为其他空间的别名, 一旦引用初始化后,与原本的变量没有任何区别只是名字不一样

3、定义引用的时候,&只是一个标识,说明在定义引用,一旦定义完毕,&就表示取一个变量的地址

//引用的注意事项
void test3()
{
    //1、引用必须初始化,他必须是对已有的空间进行去别名
    //int &b;

    int a = 100;
    //int *p = &a;
    //int &b = *p;
    int &b = a;
    cout << b << endl;
    
    //2、引用只能初始化一次
    //意味着初始化之后,
    //引用就不能再作为其他空间的别名
    //一旦引用初始化后,与原本的变量没有任何区别只是名字不一样
    //int c = 200;
    //&b = c;
    
    //3、定义引用的时候,&只是一个标识,说明在定义引用,
    //一旦定义完毕,&就表示取一个变量的地址
    //int &c = a;
    //&c;
}
int main()
{
    test3();
    
    //常量也可以引用,但是需要加const
    //const int &b = 100;

    return 0;
}

2.3 引用的使用

  1. 复制传参,将实参的值传递给形参,不管形参如何改变值,实参都不会有变化,是因为实参和形参不是同一块空间
  2. 地址传参,将实参的地址传递给形参,形参如果对地址里面的内容修改,实参的值也会变化,因为是用一块地址
  3. 引用传参,将实参定义引用,所以现在形参就是实参的别名,意味着就是用一块空间,所以引用修改空间的值,实参一定会变另外引用没有再开辟空间,所以相对而言更加节省空间

📣注意:引用可以作为返回值,这样做一定程度上节省空间并且防止内存泄漏

//引用的使用
//函数的传参方式1:复制传参,将实参的值传递给形参,不管形参如何改变值,
//实参都不会有变化,是因为实参和形参不是同一块空间
void MyChange1(int a, int b)
{
    int temp;

    temp = a;
    a = b;
    b = temp;
}
//函数的传参方式2:地址传参,将实参的地址传递给形参,形参如果对地址里面的
//内容修改,实参的值也会变化,因为是用一块地址
void MyChange2(int *a, int *b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

void MyChange3(int *a, int *b)
{
    int m = 888, n = 999;

    a = &m;
    b = &n;

    *a = 111;
    *b = 222;
}

//函数的传参方式3:引用传参,将实参定义引用,所以现在形参就是实参的
//别名,意味着就是用一块空间,所以引用修改空间的值,实参一定会变
//另外引用没有再开辟空间,所以相对而言更加节省空间
void MyChange4(int &a, int &b)
{
    cout << &a << ", " << &b << endl;
    int temp;

    temp = a;
    a = b;
    b = temp;
}

void test4()
{
    int m = 100, n = 50;
    cout << "交换前:";
    cout << "m = " << m << ", ";
    cout << "n = " << n << endl;

    cout << &m << ", " << &n << endl;

    //MyChange1(m, n);
    //MyChange2(&m, &n);
    //MyChange3(&m, &n);
    MyChange4(m, n);

    cout << "交换后:";
    cout << "m = " << m << ", ";
    cout << "n = " << n << endl;
}

//引用可以作为返回值
//这样做一定程度上节省空间并且防止内存泄漏
int &MyFun()
{
    static int a = 100;
    cout << "&a = " << &a << endl;

    return a;
}

void test5()
{
    int &b = MyFun();
    cout << "b = " << b << endl;
    cout << "&b = " << &b << endl;
}

三、C++动态开辟空间

使用malloc在堆区开辟空间,malloc开辟的堆区空间是不会初始化的

使用 calloc在堆区开辟空间,calloc开辟的堆区空间会自动初始化为0

释放空间

  • 释放:这块空间我不用了,别人可以使用
  • 开辟多少空间就必须释放多少,不能只释放一部分

注意:同一块空间不允许释放两次,否则直接报错退出

空间释放之后,地址还是保存原本的空间,所以既然已经释放了,就不要在用了。

防止野指针的目的就是不要再使用释放后的空间

realloc是在已有的空间基础上增加空间,返回的地址有可能是原本的首地址,也有可能是新地址。

C++中动态开辟空间,使用new

  1. new开辟的空间不会自动初始化

  2. new支持申请空间的同时初始化

  3. 如果要释放申请多个指定类型的空间,需要在首地址的前面加[]

  4. delete a;

将一个字符串常量的地址赋值给指针变量,如果这样做,就会导致原本堆区空间泄漏,所以绝对不要这样赋值。

#include <iostream>
#include <cstdlib>
#include <string.h>

using namespace std;

//C语言动态开辟空间(堆区开辟空间)
void test1()
{
    //使用malloc在堆区开辟空间
    //malloc开辟的堆区空间是不会初始化的
    int *a = (int *)malloc(4);
    cout << "*a = " << *a << endl;
    *a = 666;
    cout << "*a = " << *a << endl;

    //使用 calloc在堆区开辟空间
    //calloc开辟的堆区空间会自动初始化为0
    int *b = (int *)calloc(1, 4);
    cout << "*b = " << *b << endl;

    //释放空间
    //释放:这块空间我不用了,别人可以使用
    //开辟多少空间就必须释放多少,不能只释放一部分
    free(a);
    free(b);
    //注意:同一块空间不允许释放两次,否则直接报错退出
    //free(a);

    //空间释放之后,地址还是保存原本的空间,
    //所以既然已经释放了,就不要在用了
    //*a = 999;
    //cout << "*a = " << *a << endl;

    //防止野指针的目的就是不要再使用释放后的空间
    a = NULL;

    //realloc是在已有的空间基础上增加空间
    //返回的地址有可能是原本的首地址,也有可能是新地址
    //realloc()
}

//C++中动态开辟空间,使用new
void test2()
{
    //new开辟的空间不会自动初始化
    int *a = new int;
    cout << "*a = " << *a << endl;
    *a = 555;
    cout << "*a = " << *a << endl;

    //new支持申请空间的同时初始化
    int *b = new int(100);
    cout << "*b = " << *b << endl;

    //申请多个指定类型的空间
    int *c = new int[6];
    c[0] = 100;
    c[1] = 200;
    c[5] = 600;
    cout << c[0] << ", " << c[1] << ", " << c[5] << endl;

    //释放空间
    delete a;
    delete b;
    //如果要释放申请多个指定类型的空间,需要在首地址的前面加[]
    delete []c;

    char *str = new char[32];
    printf("str = %p\n", str);

    //将一个字符串常量的地址赋值给指针变量,
    //如果这样做,就会导致原本堆区空间泄漏,
    //所以绝对不要这样赋值
    //str = "hello world";
    //printf("str = %p\n", str);

    strcpy(str, "hello world");
    printf("str = %p\n", str);

    cout << str << endl;

    delete []str;
}

int main()
{
    test2();

    return 0;
}