C语言到C++(基础语法二)

一、函数重载

        概念

       函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的 形参列表(参数个数类型顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。

int add(int a, int b, int c)
{
    return a+b+c;
}


int add(int a, int b)
{
    return a+b;
}


int main()
{
    add(1,2,3);
    add(1,2);
    return 0;
}

        思考下面一段代码,下面两个函数是否属于函数重载,他们有没有什么问题?

int func()
{
    return 0;
}

int func(int a=10)
{
    return a;
}


int main()
{
    func();
    func();
    return 0;
}

         根据定义,函数名相同参数不同,他们当然构成重载函数,虽然他们构成重载函数,但是在使用时会有问题,因为调用函数时存在二异性。应该避免这种情况出现。

        函数重载原理

        先大概了解编译器编译程序过程(Linus下),现在有test. c、func.c、func.h三个文件。

1.预处理阶段,func.h头文件被展开、然后进行宏替换、条件编译、去注释。生成func.i和test.i两个文件。

2.编译阶段,检查语法,生成汇编代码。生成func.s和test.s文件。

3.汇编,汇编代码转化为二进制机器码,形成符号表。生成func.o和test.o文件。

4.链接,文件在链接阶段被分成几段进行操作。先进行合并段表,把相关文件对应的段连接到一起,然后对符号表进行合并和重定向。生成func.o和test.o链接生成a.out文件。

        C语言不支持函数重载,因为编译时两个重载函数函数名相同,在func.o符号表中存在歧义和冲突,其次链接时也有冲突,因为C语言中是直接使用函数名标识和查找。                                                       

        而C++的目标文件符号表中不是直接使用函数名来标识和查找函数,它引入了函数名修改规则,通过参数的属性修改函数名。前面提到C++中函数重载的概念中必须有参数个数 或 类型 或 顺序的不同,通过这三种参数属性修改了函数名,不同编译器的修改规则不同。Linus下修饰规则为_Z+函数长度+函数名+参数类型首字母。       

       ​​​                         通过这里我们就明白了为什么C语言不支持函数重载,因为同名函数无法区分,而C++使用函数修改规则通过参数的不同属性改变了函数名,从而就能支持重载。

二、引用

        概念

        引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。类型&引用变量名(对象名)=引用实体。引用在语法层没有开辟新空间,相当于给原变量起一个新名称。下图我们会发现a和b的地址一样。


#include <iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;

    system("pause");
	return 0;
}

 引用的特性

        1.引用在定义时必须初始化。

void test()
{
    //int& b;//编译会出错
}

        2.一个变量可以有多个引用。

void test()
{
    int a=10;
    int& b=a;
    int& c=a;
    int& d=b;
}

        3.引用一旦引用一个实体,再不能引用其他实体。

void test()
{
    int a=10;
    int& b=a;

    int c=15;
    b=c;//这里不是b引用c,而是把c的值赋给b,要和指针的使用区分
}

引用的使用

        1.引用做参数,功能与传指针参数类似。


//传指针
void swap(int* x,int* y)
{
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

//传引用
void swap(int& x,int& y)
{
    int tmp=x;
    x=y;
    y=tmp;
}

int main()
{
    int a=10;
    int b=20;
    swap(&a,&b);
    swap(a,b);

    return 0;
}

2.引用做返回值 

        在传值返回的函数中,因为函数执行完成后函数栈帧已经被销毁,会非法访问返回的变量,所以返回的值需要先存储在临时变量中。如果返回的对象较小不大于八个字节,一般是寄存器充当临时变量,如果比较大则存放在调用函数的栈帧内。所有的传值返回都会生成一个拷贝,当返回的数据量很大时使用引用做返回值效率会提高。

传值返回:

                    

       

         下面这段代码存在非法访问的问题,add函数的返回值是变量c的引用,当函数执行完成栈帧销毁后,访问了c的空间,如果add函数栈帧销毁时清理空间,那么取c值时取到的就是随机值。


#include <iostream>
using namespace std;

int& add(int x, int y)
{
	int c = x + y;
	return c;
}
int main()
{
    //非法访问c的空间
	int ret = add(10, 20);

	return 0;
}

        上面的例子是一个错误的用法,那么该如何使用引用返回?如果函数返回时,出了函数作用域,如果返回的对象还在(静态变量、全局变量等),则可以使用引用返回,否则必须使用传值返回。


#include <iostream>
using namespace std;

int& add(int x, int y)
{
	static int c = x + y;
	return c;
}
int main()
{
	int ret = add(10, 20);

	return 0;
}

        总结

        引用的作用主要体现在传参和传返回值

        1.引用传参和引用传返回值在有些场景下可以提高性能(大对象+深拷贝对象)

        2.引用传参和引用传返回值做输出型参数和输出型返回值。有些场景下形参的改变可以改变实参,引用返回可以改变返回对象。

        常引用

int main()
{
    const int a=10;
    int& b=a;//权限的放大,编译会出错,a只读而b可写

    const int& b=a;//权限不变,可编译通过

    int c=10;
    const int& d=c;//权限的缩小,编译可以通过
    return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值