本文介绍: 在前面我们写过关于命名空间和缺省参数的内容,今天我们的内容主要包括函数重载,引用和一些新语法,其中函数重载是应对函数命名冲突所产生的,引用是由于指针太过于麻烦祖师爷对其进行修改,是一种方便高效的新方法,我们的新语法包含auto关键字的使用,for函数在c++11中的新的方式,以及在c++98中一个错误以及在c++11中是如何进行修改的。在这篇文章中我会使用Linux的g++为大家讲解函数重载,这些问题在下面的文章中我会一一为大家解答。这次的内容是非常的多,希望大家可以有所收获,最后希望大家可以一键三连。

个人主页点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶    

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

目录

 一.前言

二.函数重载

2.1函数重载的类型

2.2函数重载原理

三.引用

3.1引用的基本使用和语法

3.2引用的使用场景

3.2.1参数

3.2.2返回值

3.3引用经常出现的错误

3.4引用和指针的差别和运行比较

3.4.1引用和指针语法上的不同

3.4.2引用和指针在底层上

 3.5传引用和传值运行的差别

3.6常引用

3.6.1常引用的写法

3.6.2临时变量具有常性

四.auto关键字

4.1基本使用

4.2不适用的场景

五.新语法

5.1for循环的新写法

5.2空指针的错误

六.总结


 一.前言

        在前面我们写过关于命名空间和缺省参数的内容,今天我们的内容主要包括函数重载,引用和一些新语法,其中函数重载是应对函数命名冲突所产生的,引用是由于指针太过于麻烦祖师爷对其进行修改,是一种方便高效的新方法,我们的新语法包含auto关键字的使用,for函数在c++11中的新的方式,以及在c++98中一个错误以及在c++11中是如何进行修改的。在这篇文章中我会使用Linux的g++为大家讲解函数重载,这些问题在下面的文章中我会一一为大家解答。

二.函数重载

2.1函数重载的类型

        函数重载就是函数的名字相同,但是函数的参数不同,参数不同包括参数个数,参数类型这两类,例如下面的代码:

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

double Add(double a, double b)
{
	return a + b;
}

这里就是函数的参数类型不同,对于下面的代码也是函数的参数类型不同。


double Add(int a, double b)
{
	return a + b;
}

double Add(double a, int b)
{
	return a + b;
}

接下来的代码属于函数的参数个数不同,代码如下:

double Add(int a, double b)
{
	return a + b;
}

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

2.2函数重载原理

        为什么会支持函数重载呢?这是如何实现的呢?我们知道一个程序的运行需要经过预处理,编译,汇编,链接这四种,在预处理阶段我们是将头文件展开,宏的替换,条件编译,注释的替换这几部分,在编译阶段是检查语法,将代码转成汇编代码,汇编代码是将汇编代码转为二进制代码,链接是将代码链接,符号表进行合并。我们现在vs中演示一下为什么会有函数重载,我们先看,由于预处理是将头文件展开,宏的替换,条件编译,注释的替换,对于函数重载没有影响,编译阶段,由于编译阶段需要对代码检查语法,所以我们的函数重载和这个有关,我们分三个文件进行代码的编写,第一个文件内容为

#pragma once
#include<iostream>

int Add(int a, int b);
int Add(int a, int b, int c);

 第二个文件的内容为

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

第三个文件的内容为

#include"test.h"

int main()
{
	int ret1 = Add(1, 2);
	int ret2 = Add(1, 2, 3);
	return 0;
}

我们进入调试然后看看函数的汇编代码:

 我们可以看到它是去call这个Add函数,Add后面是函数的地址,我们可以看到第一个Add函数的地址为0X7FF7CFD211C7,第二个Add函数的地址为0X7FF7CFD21208,我们可以看到这个两个函数,在Linux中我们也用这个进行演示,我们看到

所以我们可以看到第一个Add为_Z3Addii第二个Add为_Z3Addiii所以我们可以看到这是两个函数。

我们可以看到它call的是函数名,所以c语言不支持函数重载。

三.引用

3.1引用的基本使用和语法

        你还记得我们C语言中的指针吗,还记得被指针支配的痛苦吗?一级指针,二级指针,想想这些是不是还后背发凉,在我们的链表那里我们用到了二级指针,想要写出这些对指针的了解必须深刻,你是不是也感觉指针是非常的难受呢?我们的祖师爷也因为对指针比较烦所以写出了我们引用的这个语法,那引用应该怎末去写呢?让我们看下面的代码:

#include<iostream>
using namespace std;
int main()
{
	int a = 1;
	int& b = a;
	cout << "a: " << a << " b: " << b << endl;
	b = 2;
	cout << "a: " << a << " b: " << b << endl;
	return 0;
}

我们运行代码可以看到:

我们需要知道引用的基本语法,引用需要定义时进行初始化,我们可以将引用看成起别名,比如上面的代码b就是a的别名,例如我们知道的李逵,他也叫黑旋风,也叫铁牛,这三个都是他的名字,我们的引用也就和这个一样。

3.2引用的使用场景

3.2.1参数

        例如我们定义一个不带头的链表,这个我们以前是使用二级指针写的,现在我们就可以用引用进行写,代码如下:

#include<iostream>
using namespace std;
struct SNode {
	int val;
	SNode* next;
};

SNode* Create(int x)
{
	SNode* p = (SNode*)malloc(sizeof(SNode));
	if (p == NULL)
	{
		perror("malloc");
		return NULL;
	}
	p->val = x;
	p->next = NULL;
	return p;
}
void Push(SNode*& head, int x)
{
	if (head == NULL)
	{
		head = Create(x);
	}
	else
	{
		SNode* tail = head;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = Create(x);
	}
}

int main()
{
	SNode *head=nullptr;
	Push(head, 1);
	return 0;
}

在这里引用做参数我们就可以不使用二级指针,是不是感觉变简单了很多?

3.2.2返回值

        引用除了做参数我们还可以做返回值例如下面的代码:

int ret = 0;
int& myAdd(int x, int y)
{
	ret = x + y;
	return ret;
}
int main()
{
	ret = myAdd(1, 2);
	std::cout << ret;
	return 0;
}

3.3引用经常出现的错误

我们看下面的代码:

#include<iostream>
using namespace std;
int& fun1()
{
	int a = 0;
	return a;
}
int main()
{
	int& ret = fun1();
	cout << ret << endl;
	return 0;
}

我们想想这个代码输出的结果是什么?我们看函数的返回值是用别名的我们返回的就是fun1函数中的a,我们知道函数是在栈区创建的,当函数运行完后就会进行销毁,至于内存是如何销毁和对内存进行修改的这个与编译器有关,a的值会存在寄存器中,在vs2022中运行后我们看到:

但是我们运行下面的程序,代码如下:

#include<iostream>
using namespace std;
int& fun1()
{
	int a = 0;
	return a;
}
int& fun()
{
	int b = 1;
	return b;
}
int main()
{
	int& ret = fun1();
	cout << ret << endl; 
	fun();
	cout << ret << endl;
	return 0;
}

我们运行代码后可以看到

我们的ret是别名当我们在这块空间上再次执行函数时我们的别名还在,但是内容修改了,所以我们可以得到这个结论,引用不能做函数返回值。

3.4引用和指针的差别和运行比较

3.4.1引用和指针语法上的不同

  • 引用是取别名,在语法上不占用内存,指针占内存。
  • 引用必须初始化,指针可以初始化也可以不初始化。
  • 引用不需要考虑空指针的问题,指针需要。
  • 引用不能改变指向,指针可以改变指向。

3.4.2引用和指针在底层上

我们看下面代码:

int main()
{
	int a = 0;
	int& b = a;
	int* p = &a;
	cout << sizeof(b) << endl;
	cout<<sizeof(p) << endl;
	return 0;
}

我们进入调试看看汇编代码:

我们可以看到它这两个在底层上没有任何区别,没有引用这个东西,只有指针。

 3.5传引用和传值运行的差别

        我们知道了引用引用是将变量传过去,那我们对于传值和传引用那一个更方便快捷呢?我们看下面的代码:

#include<ctime>
struct A {
	int arr[10000];
};
void Time1(A a){}
void Time2(A& a){}
int main()
{
	A a;
	int begin1 = clock();
	for(int i=0;i<10000;i++)
		Time1(a);
	int end1 = clock();
	cout << (end1-begin1) << endl;
	int begin2 = clock();
	for(int i=0;i<10000;i++)
		Time2(a);
	int end2 = clock();
	cout << ( end2-begin2) << endl;
	return 0;
}

3.6常引用

3.6.1常引用的写法

        我们使用引用时我们是传的变量,这时候我们就会发现这样其实有一定风险,那就是引起变量的改变,我们不想改变变量的内容,这时候我们的常引用就出现了例如上面的Time2函数我们就可以将参数变成const A &a;还有那写是常引用呢?看下面的例子:

	int a = 10;
	const int& b = a;
	//b=9;

我们利于const进行修饰b这样我们的b就是一个常引用,在这时b是a的别名,但是我们不能修改b的值,也就是说b=9这行代码是错误的,const相当于把b的权限缩小了,那我们给b起一个别名c

	int& c = b;	

事实上这样写也是错误的,这样我们相当于把b的权限扩大了,这样是不合法的,我们需要知道这个的权限可以缩小,但是不能扩大。

3.6.2临时变量具有常性

        我们需要知道一个表达式的结果和一个函数的返回值都是存在一个临时变量中的,我们需要知道临时变量具有常性,我们看下面代码:

int a=10,b-5;
int & c=a+b;

这段代码是错误的 ,主要就是临时变量具有常性,我们需要知道a+b的结果是存在一个临时变量中的,那我们应该如何去存呢?如下:

const int & c=a+b;

 我们使用常引用就可以解决。

四.auto关键字

4.1基本使用

        auto这个关键字是在c++11中提出来的,这个关键字的作用是自定匹配变量的类型,听上去是不是感觉很爽,在这里我们补充一个内容我们可以使用typeid(变量).name()这个函数得到i变量的类型。那auto这个关键字应该如何使用呢?我们看下面例子:

	int a = 10;
	auto b = a;
	cout << typeid(a).name() << endl;

我们运行后可以看到是int类型,auto也可以和引用一起用:

int a = 10;
auto& b = a;
cout << typeid(b).name() << endl;

在auto中我们有一个很爽的使用场景,还记得我们的函数指针吗?我们就可以使用auto来表示函数指针的类型,看下面的代码:

int Add(int a, int b)
{
	return a + b;
}
int main()
{
	auto p = Add;
	cout << typeid(p).name() << endl;
	return 0;
}

在这里我们的auto会非常的爽。

4.2不适用的场景

首先auto不适用于函数的参数,例如下面的就不适合:

int Add(auto a, auto b)
{
	return a + b;
}

第二auto不能作数组的类型,例如下面:

	auto a[] = { 1,2,3 };

五.新语法

5.1for循环的新写法

在c语言中我们使用的for循环的格式是:

	for (int i = 0; i < n; i++)
	{
		//....
	}

在c++11中出现了新的模式,利用范围进行写循环例如

int main()
{
	int a[] = { 1,2,3 };
	for (auto i:a)
	{
		cout << i << endl;
	}
	return 0;
}

这样子的实质是将数组的元素拷贝到i中,然会进行输出,当然auto改成int也可以。如果我们想修改数组元素的值呢我们可以使用引用例如下面的代码:

	int a[] = { 1,2,3 };
	for (auto& i:a)
	{
		i *= 2;
	}
	
	for (int i = 0; i < 3; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;

我们加入引用就可以对数组的值进行修改。

5.2空指针的错误

我们看一段代码

void Print(int a)
{
	cout << "a" << endl;
}
void Print(int* p)
{
	cout << "*p" << endl;
}
int main()
{
	Print(0);
	Print(NULL);
	return 0;
}

这段代码的输出结果应该是什么样的?我想一般都会说输出为a和NULL,但是实际上是

这主要就是在编写时出现的一些问题,在这里不做解释,那如何改正呢?,我们只需要在以后中空指针写成nullptr就可以解决了。

六.总结

        这次的内容是非常的多,希望大家可以有所收获,最后希望大家可以一键三连。

原文地址:https://blog.csdn.net/Infernal_Puppet/article/details/135852720

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_63891.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注