【C++】命名空间 & IO & 函数重载 & 引用

命名空间

在c语言当中:当我们定义了一个函数,但是这个函数与库函数发生了重名,或者这个函数和我们之前定义的函数发生了重名,这时候我们就只能更换这个函数名,以此来避免。

但是在C++中,我们处理的方式有所区别:我们使用的是命名空间的方式,这样就可以避免发生命名冲突的情况了

命名空间的使用方法

命名空间的构建方式:与结构体的构建方式非常相似。

namespace A
{
	int a = 10;
	int Add(int x, int y)
	{
		return x + y;
	}
}

1.using namespace [命名空间名称]:这样做相当于把命名空间中所有的定义都展开到了全局作用域中,这样做的话,如果命名空间中存在和全局作用域重复的名称,也会发生命名冲突。
2.using [命名空间]::[变量名]:这样做相当于将命名空间中的部分变量放到了全局作用域中,我们推荐这种做法
3.不适用using,当我们需要用到命名空间中的部分变量名时,使用命名空间+::变量名:例如 std::cout << 【输出值】 <<std::endl;

注意:当我们在同一个或者不同源文件中,多次使用同一个命名空间,编译器会将同名的命名空间合并到一起,组成一个更加庞大的命名空间

IO

什么是IO?
实际上IO就指的是input和Outpot,输入输出。

c和c++的区别

当我们在C语言中使用标准IO时,使用的函数是scanf和printf,但是我们会发现,这样的方式好像存在一些不安全的因素。
在这里插入图片描述
当我们使用printf()函数的时候,如果只打印一个值,但是给了多个值的时候,依然可以打印出来结果,可是按照严谨的逻辑来说,编译器应该报错,告诉我们这个写法不正确,但是c语言的编译器并没有这样做,这样就会导致程序的安全性不足。

还有另外一个缺点,我们需要记住不同数据类型的编码,如果记错了,也会无法打印。

而C++语言就避免了这些缺陷,我们可以直接使用cin和cout搭配<<>>来进行输入和输出。

当我们需要使用c++的代码时,标准输入输出库函数就需要修改为,并且cin cout是包含在std这个命名空间的,我们需要将这些函数都放到全局作用域中,才能正常使用这些函数。

#include<iostream>
using std::cout;
using std::endl;
using std::cin;

int main()
{
	int data;
	cout << "hello world" << endl;
	cin >> data;
	cout << data << endl;
	return 0;
}

此外,当我们使用c语言定义函数的时候,可以不指定函数的返回值类型,编译器会默认返回值类型的int,而c++不可以,必须指定返回值类型,否则就会报错。
同时,c语言是不支持函数重载的,而c++是支持的
这里我们简单介绍一下什么是函数重载

函数重载

在c++语言中,在同一个作用域中,出现函数名称相同,但是函数的参数类型,函数参数数量,函数参数顺序不同,标志着这是不同的函数实现的功能也不相同。

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

这三个函数会发生函数重载,如果我们调用Add这个函数,那么编译器会区分我们传的值来确定调用哪个Add函数,如果完美匹配到可以调用的函数,则进行调用,如果没有找到可以完美匹配的,并且可以发生隐式整形提升,那么也可以调用,否则报错。

Add(10, 20)//可以进行调用
Add(10.0, 20.0)//可以进行调用
Add(10, 20.0)//可以进行调用
Add('a', 'b')//无法进行调用

那么就有一个问题,为何c语言不支持函数重载,但是c++就支持呢?

实际上,是因为编译器在编译的时候对函数的命名方式不同
c语言在编译的时候,函数的命名是在函数名称前加一个_,因此,多个同名函数是无法进行区分的,那么就无法进行函数重载。

而c++语言的函数命名是一个较为复杂的情况,编译器会将这个函数的名称,参数类型,返回值类型都编译到最终的函数名称中,这样在编译的时候,根据我们传的参数,编译器就可以知道调用哪个函数。

引用

引用并不是定义一个变量,而是给已经存在的变量取了一个别名,编译器也不会为引用开辟新的内存空间,引用和被引用的变量共用一块内存空间。

引用的使用方法

int a = 10;
int& ra = a;//注意,引用类型和引用实体必须是同种类型

在c语言中,我们传参的方式共有两种,传值和传地址,传值实际上是对实参的一份临时拷贝,并不能修改实参,若需要通过形参改变实参的值,就需要传地址

void Add(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	Add(a, b);
	cout << a << endl << b << endl;
	return 0;
}

而在c++中,既有上述两种传参的形式,又增加了一种名为引用的概念,我们可以通过传引用来实现和传地址相同的操作,来修改实参的值。

现象:

int& Add(int a, int b)
{
	int res = a + b;
	return res;
}
int main()
{
	int& r = Add(10, 20);
	Add(30, 40);
	Add(50, 60);
	printf("%d\n", r);
	printf("%d\n", r);
	return 0;
}

在这里插入图片描述
为何打印两次的结果不相同,并且在debug过程中,查看r的值,在三次函数调用的过程中都会得到正确的计算结果?

解释:第一次调用Add函数的时候,我们将返回值res进行了引用,而这个res是在栈上被开辟的,当Add函数调用结束,Add开辟的函数栈帧就会被释放,此时r这个引用实际上对应的空间也被释放了,但是操作系统并没有对其中的垃圾数据进行清理,而下次这块内存空间需要被使用时,内存中的数据才会被清理,这样就看到了r对应的值得到了正确的计算结果(因为每次调用相同的函数,因此开辟的函数栈帧也是相同大小的,r指向那块内存恰好也是上次res的位置)。当第一次调用printf函数时,首先函数进行了传参,此时将r对应位置上的值进行了传递,因此将r位置的值记录了下来,随后,因为需要开辟printf的函数栈帧,所以会将上一次内存空间中的垃圾数据进行清理置为随机值,第二次调用printf函数时,传参r读取到的实际上已经被初始化了,因此看到的是一个随机值。

产生原因:将引用作为返回值返回,并且返回的是栈上开辟的空间。

避免方法:引用作为方法的返回值时,返回的实体一定不能返回栈上空间,返回的实体的生命周期要比方法的运行周期长,例如:返回全局变量,返回static修饰的变量。

引用和指针的区别

在实现的底层汇编指令来看,引用和指针是相同的实现方式,都是以指针的方式来实现的,但是从概念层面来看,二者是有区别的。

从概念上看,引用是变量的一个别名,和被引用的变量共用内存空间。
指针需要进行取地址和解引用操作,引用没有这两个操作,是编译器自动实现的。
引用一个变量后,这个引用就不能再改变了
相当于int& ra = a; ra的类型实际为int* const,因为ra的指向是不能改变的。

总结一下引用和指针的区别:
1.引用概念上定义一个变量的别名,指针存储一个变量的地址
2.引用在定义时必须初始化,指针没有要求
3.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
4.没有NULL引用,但是有NULL指针
5.在sizeof中的含义不同,引用结果为引用类型的大小,指针是地址空间所占字节个数
6.引用自增为引用的实体自增,而指针自增为指针向后偏移一个类型的大小
7.有多级指针,但是没有多级引用
8.指针需要显式解引用,而引用可以通过编译器自己处理
9.由于3的存在,使得引用比指针使用起来更加安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值