C++命名空间,输入输出,默认参数,函数重载,内联函数,超详细介绍。

 

命名空间

#include <iostream>
using namespace std;
int main()
{

    cout<<"Hello world!"<<endl;
    return 0;

}

上面是一个简单的利用C++来实现输出“Hello world!”的代码,我们都学习过C语言,知道第一行是用来包含头文件的,接下来那句先跳过,然后下面是main函数,main函数体内第一条语句是C++语言的标准输出语句,最后返回0,代表此程序运行后正常结束。那么第二句语句是什么意思呢?

这里就需要引入命名空间这个概念。在C语言中,不知道大家有没有过不知道给变量取什么名字的烦恼,在不同的文件中,随着代码量的增加,很难保证命名重复的问题。当以后在多人合作的项目中,很多人都有自己的任务来共同完成一个项目,那么命名重复难以避免,在C语言中命名重复就会出问题,而C++为了避免命名冲突的问题,新增了命名空间的概念。

不同的命名空间,简单来说就是划分了一块块空间,只要保证在各自的空间内命名不冲突,在不同的空间内给变量设置相同的名字是被允许的。上面的程序就是在std这样的命名空间里面,在不同的命名空间中相同的名字可能代表不同的含义,在std这个空间内,cout的作用就是能够将后续内容打印在屏幕上。std是系统库里的命名空间,是系统已经有的,using namespace std;的意思就相当与将该命名空间展开,默认下面的变量都是该命名空间内的。 如果我们不写这句,向下面这样,那么编译器就找不到cout这个东西,因为我们也没有定义它。

#include <iostream>
int main()
{

    cout<<"Hello world!"<<endl;
    return 0;

}

f9f522839d91436d862ce9f549d9272a.png

除了系统库里已经有的,我们也可以自己自己定义一个命名空间,具体如下:

namespace myspace
{
    int a = 0;
    int b = 2;
}

这样我们就自己创建了一块命名空间,关于使用空间内的变量,一种方法就是第一个程序,使用using namespace <命名空间>;的方法,这样就默认下面的变量都是该空间内的,还有一种方法就是使用域作用限定符::(两个冒号),来表示使用的是该命名空间内的变量

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

4c750740228649369fb6b41446993c6b.png

成功地利用std中的cout输出了myspace中的a。

要注意,using namspace< >相当于将该命名空间展开,这样做虽然方便,但是在实际开发中有一定的危险性,所以建议大家使用域作用限定符来访问命名空间中的数据。

相信到这里,大家对于命名空间也有了一定的了解与认识,不过有一个问题,那引用的头文件与命名空间有什么关系呢?

实际上,命名空间只是将一些数据内容封装在了一起,只是一种组织代码的方式,而头文件是用来声明这些数据内容的。换个方式来说,可能在头文件中包含对命名空间的创建。比如在头文件<iostream>中包含 namespace std{...}。这样一说大家就明白头文件和命名空间的关系了吧。

输入输出

在文章最开始的演示代码中,我们看到了如下cout代码

cout<<"Hello wworld!"<<endl;

cout是来控制C++的输出的,我们现在只需要知道它的用法直接用就好,具体详细的内容在之后讲流的时候会具体介绍。cout能将内容输出在屏幕上,还有与之对应控制输入的是cin,而最后的endl表示换行,功能与C语言中的“\n”类似。<<这个符号代表流插入,>>这个符号代表流提取,现在只需要会用就好,具体内容后续会详细介绍。这里输入输出比C语言方便的一个点是它会自动判断类型,不需要我们来指定它的输入输出类型。

缺省参数(默认参数)

能够设置缺省参数(也叫默认参数)的功能是C++相较于C语言的改进,意思是在函数声明的时候我们可以给形式参数设置默认值,在实际调用函数传参的时候可以少传些参数,对于没有传参的参数就使用默认值。这也是为什么要叫缺省参数(默认参数)的原因。但是在使用过程中也有一些规则:

第一点规则,在设置默认参数的时候只能在形参列表中连续的从最右面往左来设置缺省参数,中间不能断开也不能从左面开始设置。

void Set(int a,int b,int c = 0,int d);
//错误,不能从中间跳着设置,必须从最右边开始
void Set(int a,int b = 0,int c,int d = 0);
//错误,中间不能断开,必须连续设置
void Set(int a = 0,int b = 0,int c = 0,int d);
//错误,必须从最右面开始,不能从左面开始设置
void Set(int a,int b,int c = 0,int d = 0);
//正确,从最右面开始向左,且连续设置

要求这样的原因是为了保证在使用过程中不出现歧义,在实际调用函数传参的时候从左向右传,若缺省参数从左开始设置或者中间不连续,都可能出现歧义。如下:

void Set (int a = 0,int b = 0,int c);
Set(1,2);

若代码这样写,1该传给a还是b呢,2又该传给b还是c呢?

void Set (int a,int b = 0,int c = 0);
Set(1,2);

这样就不会有歧义,1传给a,2传给b,c使用默认参数0, 

使用过程中以实际传入实参为主,缺省参数只是没有给那个变量传参时的默认值,若实际传参,则肯定是赋予传的实际参数的值。就像上面,当给b传2的时候,给b设置的缺省参数便被覆盖,不起作用了

第二点规则,在设置缺省参数的时候,当声明和定义分开的时候,只能在函数声明中设置,不能在函数定义时设置。这点规则也是为了避免出现歧义,机器是老实的,它只会执行规定的操作,当有歧义出现的时候它就不知道怎么做了,如果我们在声明和定义中都去设置的话,设置的一样还好,万一不一样,机器就不知道怎么执行了,所以C++规定,当函数声明和定义分开的时候缺省参数只能在函数声明时设置

函数重载

我们知道,在C语言中,不能有多个函数名相同的函数,在实际情况中,有时候函数功能类似而只是参数数据类型不同,这个时候函数名又成了个问题,比如都是实现加法,add这个函数名就很好,可因为类型不同,我们可能需要有add1,add2,add3......来分别代表不同数据类型的加法。在C++中,函数重载就很好的解决了我们的这个烦恼,相似功能的函数可以有共同的函数名,因而叫函数重载。

函数重载有一定规则,当函数参数类型或者参数个数不同时,就可以使用函数重载。返回值不做要求,如下:

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

cout<<add(1,2)<<endl;
cout<<add(1.1,2.2)<<endl;

93ffd0bc559c470083946dd3469c2af3.png

 这样就实现了函数重载,但是函数重载也要注意一些二义性的问题,如下:

int fun();
int fun(int a = 0);
//上面为两个函数的函数声明
//下面为函数的调用
fun(1);
//正确
fun();
//错误,这个时候就出现了函数调用的二义性问题

 结合上面的缺省参数,这两个fun函数因为参数数量不同能够实现函数的重载,可是在函数调用的时候,若传了参数还好,编译器知道要调用第二个fun函数,可是当不传参的时候,是调用本就无参数的fun函数还是调用设置了缺省参数的fun(int a = 0)呢?这个时候就有问题了,要避免写这样的函数重载。再比如上面第一个举的add重载的例子:

add(1,2.2);//错误
add(1.1,2);//错误

上面的add重载乍一看没有问题,可是再看上面的调用呢?第一个参数为Int,第二个参数为double,那我是调用哪个add函数呢,最后应该返回什么类型?好像无论是返回Int还是返回double都有些不合适,所以在函数使用时也要注意避免二义性的问题出现,计算机是很聪明又很死板的。 

关于为什么C不能而C++能实现函数重载这与在编译时候他们不同的处理有关,在编译的汇编阶段会生成一个对应函数地址的符号表,在C语言中,是通过函数名来链接函数地址的,而在C++中是通过函数名和参数列表来共同链接函数地址的,所以C++中参数类型和数量的不同都会对应符号表中不同的函数地址,所以C++可以实现函数重载而C语言不可以。

内联函数

关于内联,我们先回忆一下C语言中的宏,#define,简单来说,宏是一种替换,我们可以将简单的代码实现直接定义成有参宏,比如我们想实现两个数相加:

#define add(a,b) ((a)+(b))
#include <stdio.h>
int main()
{
    printf("%d",add(5,5));
    return 0;
}

 前面也说了,宏是一种替换,本质就是在编译预处理阶段,直接进行代码替换,使用有参宏时不会调用函数栈帧,程序效率会提升,但是对于有参宏我们无法进去内部进行调试,也因此无法进行参数等安全性的检查,再者繁琐的括号也使得有参宏容易出错,因而C++中出现了内联函数这个功能。在保证有参宏优点的同时,避免了有参宏的缺点。

内联函数本质就是当程序需要调用该函数地址时,编译器自动将函数体插入来代替对函数地址的调用因此来减少函数调用的开销(函数栈帧调用创建销毁等)。与有参宏不同的是内联函数可以进入函数体进行调试,也能进行安全性的检查等。因为每次对于内联函数的调用都会将函数体代替函数地址,所以内联函数的调用会影响可执行程序总体的大小,因此太长或太复杂(包含循环或switch选择语句等)的函数体不适合声明为内联函数,事实上,inline声明的函数也并不肯定会成为内联函数,inline对于编译器来说只是请求,具体还得看编译器,一般有太长或太复杂函数体的函数,即使声明了inline也不会成为真正的内联函数。

因为内联函数独特的运行原理,内联函数的声明和定义不能写在不同的文件中,因为编译器在编译期间就得能看到它的实现才能进行内联扩展,如果声明和定义分离,编译器在处理声明文件的时候找不到定义,自然也不会进行内联扩展,想要达成内联的目的也就无法达成了。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值