初始C++(一):命名空间/缺省参数

初始输入输出

既然这篇文章叫初始C++,看我文章的应该和我一样,基本是个小白,对C++一窍不通。
所以这里对C++语法做一点点小的补充。
在C++中很多地方都用的iostream库。iostream库包含两个基础类型istream和ostream,分别表示输入流和输出流。

cout的用法

cout就相当于C语言的printf。如果你要输入什么值的话可以这样写:

cout << "hello world";
//加上一个<<,这叫流插入运算符
int i = 0;
cout << i;

和printf不同的是,printf函数在输入什么值时要表明输出值的类型,而cout不用,cout和下面要讲的cin一样可以自动识别你要输出/输入的数据是什么类型的。cout是ostream类型的对象(目前可以先不用理解对象是什么)

printf("%s", "hello world");
int i = 0;
printf("%d", i);

cout具体为什么要这样写,可以先不用管,可以想象成一个叫"hello world"的字符串像水一样流到cout里面去,cout就是那个可以显示的黑框框。

cout还可以这样写:

int i = 10;
cout << "hello world" << i;

输出就是:
在这里插入图片描述

这就相当于C语言的:

int i = 0;
printf("%s%d", "hello world", i);

输出的运算符<<计算结果就是其左侧运算对象,上面式子等价于:

(cout << "hello world") << i;

cin的用法

cin就像C语言的scanf函数。cin是istream类型的对象

int i = 0, j = 0;
cin >> i >> j;//>>是流提取运算符

这就像C语言的:

int i = 0;
scanf("%d", i);

运算符>>返回其左侧运算对象作为其计算结果。上面表达式等价于:

(cin >> i) >> j;

endl的用法

endl就是C语言的换行符,它是一个被称为操纵符的特殊值,写入endl的效果是结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。

cout << "hello world" << endl;

这就相当于C语言中的:

printf("hello world\n");

上面这三个东西的用法暂且先理解这么多就行了。

一.命名空间

1.:: (域作用限定符)

首先我们了解一下域这个概念。目前来说我们接触到的域有两种:全局域/局部域

全局域:不在{}内的地方就可以认为是全局域,在全区域里面定义的变量就可以叫做全局变量,在全局域里定义的变量它的生命周期就是程序的开始到程序的结束。
局部域:每个{}内部都可以看作局部域,在里面定义的变量就是局部变量。局部变量的生命周期是从 { 开始,到 } 结束。

看下面这个代码:

int a = 0;

void test1()
{
	int a = 1;
	printf("%d",a);
}

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

这个printf打印的是什么呢?答案应该是1,虽然a这个变量在全局和局部都有定义,但这并不冲突。在test1这个函数里使用printf打印变量a时,局部变量的优先级会大于全局变量,也就是说在使用a这个变量时会优先在当前这个局部寻找a,如果找到了就打印,如果找不到就去全局域去找。

但是现在我有一个想法,局部和全局我虽然都定义了变量a,但是我想优先打印那个全局变量的变量a,在C语言里是没法实现的。但C++在这里有一个方法,就是在变量前加上一个运算符::(域作用限定符)

int a = 0;

void test1()
{
	int a = 1;
	printf("%d",::a);
}

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

在这里插入图片描述

现在printf打印的就是那个全局变量a。

正常情况下,就是不加这个运算符时,优先会在局部域寻找。但是加上::这个运算符之后,它会到::左边那个域去找,看上面那个printf函数,发现**::左边什么都没有,也就是空,而空就代表全局域**,也就是说加上这个运算符的话,它会在全局域去寻找。自然结果打印的就是0.

2.命名空间定义

通过上面的研究,可以发现虽然有不同的域,但是在不同域里可以有相同的变量。现在有一个问题,在一个域里可不可以有相同的变量呢?

比如说在一个项目里,小A和小B在一起做一个项目,因为他们俩工作时不在一起,对方写的代码也不知道。可能就出现一种情况,小A和小B定义了一个相同名字的结构体变量。在他们的代码没有合在一起时,一点问题都没有,但是他们各自写的代码合在一起时发现出问题了,变量名字发生了重定义,这怎么处理呢?如果两个人都不想该自己的变量名咋办?

正是因为会出现这种在一个域里同时出现两种一样的变量的情况,所以C++里引入了命名空间的概念。

我先把小A和小B的代码分别写出来:

//小A
struct node
{
	struct node* next;
	int val;
};
//小B
struct node
{
	struct node* next;
	struct node* prev;
	int val;
};

这两个写的代码在自己电脑上运行没啥问题,但是合在一起的话:
在这里插入图片描述

就会有类型重定义的错误。
所以命名空间就可以发挥它的作用了,命名空间是这样定义的:

//namespace是一个C++里面的关键字
namespace _A
{
	struct node
	{
		struct node* next;
		int val;
	};
}
namespace _B
{
	struct node
	{
		struct node* next;
		struct node* prev;
		int val;
	};
}

namespace + 名称:这就是所谓的命名空间。命名空间其实就是一个域,叫做命名空间域,和全局域,局部域不同的是,它不会影响域里面变量的生命周期,只会影响里面变量的使用

要知道一个变量放在全局域里它的生命周期就是这个程序的生命周期,如果放在局部域中,这个变量的生命周期就是和它所在的那个区域有关了。所以和命名空间域不同的是,全局域和局部域会改变变量的生命周期,同时也会改变变量的使用
命名空间是在编译期间影响了查找的规则。

3.命名空间的使用

既然我们把这些变量都放在了各自的命名空间中,我们该如何使用呢?向下面这样写可以吗?

#include "A.h"
#include "B.h"

int main()
{
	struct node n;
	return 0;
}

答案当然不行,因为我说过,变量在使用时会优先在局部域去找,如果没找到就去全局域去找,如果全局域没有就会报错。编译器它默认不会到命名空间域里去寻找的。如果想用,就该用刚刚接触过的一个运算符::。这个运算符如果左边是空的话,说明优先去局部域里找,如果我们在左边加上一点东西:

#include "A.h"
#include "B.h"

int main()
{
	struct _A::node n;
	return 0;
}

这就说明我们要去_A这个域里面去找。而_A就是最开始小A自己定义的命名空间域的名称。

现在我们就很好的解决在一个域里定义多个相同变量的问题了。

这里要注意一个细节_A::是加在node的前面,而不是struct的前面。因为struct只是一个结构体关键字,两个变量名字真正冲突的是后面的node。

4.命名空间的一些其它使用

上面把命名空间的大概讲完了,但是小A,小B两个人心有灵犀,不小心把命名空间的名字也写成一样的了,这时候也没什么好的方法了,只能让他俩打一架,谁赢谁改名字~

命名空间的其它规则:

在不同文件里定义的命名空间名字是一样的,编译器会自动把他们变成一个命名空间

命名空间是支持嵌套使用的。

像下面这个代码:

namespace _A
{
	struct node
	{
		struct node* next;
		int val;
	};
	namespace _AA
	{
		int min = 0;
	}
}

此时我们该如何访问这个变量min呢?可以这样写:

#include "A.h"
#include "B.h"

int main()
{
	//struct _A::node n;
	printf("%d", _A::_AA::min);
	return 0;
}

这个变量会先去_A这个命名空间里去找,找到之后再去_AA这个命名空间里去找,最终找到了变量min的定义。

这里不要写反了,代码从左到右开始寻找。不要写成:
printf(“%d”, _AA::_A::min);

5.全局展开

后来,小B因为没打过小A,离开了公司所以现在是小A一个人在做项目,但是小A的一些变量还放在命名空间中,又怕以后有人和他命名的变量又相同,所以他不想把自己定义的命名空间删掉,但是平时在用的时候,又非常的麻烦。所以在C++中又有一个叫全局展开的东西:

using namespace _A;

这里的_A就是你命名空间的名字。而这个全局展开的意思就是,

如果没有这个代码,在使用变量时只会到局部域,全局域去找,默认不会到命名空间域里去寻找。而加上这行代码的话,不仅会到全局域和局部域去找,还会跑到命名空间域里去找
所以说,你在有这行代码后,之后用到你命名空间里的变量时,该怎么写就怎么写,不用加其它东西。

虽然事情变简单了,但是不推荐这样写,命名空间域本来就是防止重定义而加的东西,你直接全局展开,这不就是做了很多无用功吗?所以一般你在做项目时,这句话最好不要写。但是平时在做练习,做题目的时候,这个有没有也无所谓,因为此时的代码量不会很大,想这些命名重定义的情况也基本不会发生。

一些注意事项:

  1. 全局展开的代码后面记得把分号带上,不要忘记了。
  2. 全局展开不要理解成把命名空间域直接删掉,而是在使用一些变量时多了一条寻找的路子,如果你在展开后还想在前面加上_A::这类的运算符也是没问题的。

不仅小A和小B两个人可能会发生命名冲突。你写的代码还有可能和标准库的命名发生冲突。所以C++使用的标准库也有一个自己的命名空间,名字叫做:std.

所以说我们平时可能会看到这个样子的代码:

#include <iostream>
int main()
{
	std::cout << "hello word" << std::endl;
	return 0;
}

这是因为cout,endl这俩东西本身是放在std这个命名空间里的,所以我们在使用的时候,前面要加上std::。

当然我们还会看到下面这种写法:

#include <iostream>
using namespace std;
int main()
{
	cout << "hello word" << endl;
	return 0;
}

这就是上面提到的全局展开,因为cout,endl一般会用很多次,每次前面加上std::会很麻烦,所以这里直接全局展开。

相同道理,全局展开std不推荐,我们平时在做练习,写一下小东西的时候展开就行了,在写一些大型项目时还是不要这样写了,C++库把代码放到命名空间中,就是为了上一层保险,你如果直接把这层保险直接删掉,还是挺不好的。

6.部分展开

有时候觉得全局展开有点太过分了,但是不展开,在写代码时又有点麻烦,有没有什么比较好的办法呢?当然,我们可以在写之前部分展开,就是把那些常用的展开,不常用的就正常去写。比如向下面这样:

#include <iostream>
//using namespace std;
using std::cout;
using std::endl;

int main()
{
	int i = 0;
	cout << "hello word" << endl;
	std::cin >> i;
	return 0;
}

这里就把常用的cout,endl展开就行,而没有展开的则正常去用。

二.缺省参数

2.1全缺省

我们平时再写函数时,可能会这样写:

#include <iostream>
using namespace std;

void test(int i)
{
	cout << i << endl;
}

int main()
{
	test(1);
	return 0;
}

就是test这个函数在使用时,需要传递一个参数,但如果我们不知到传啥或者忘记传的时候,此时系统就该报错了,所以C++在这里为我们新加了一个概念:缺省参数。现在我们可以把函数写成这样:

void test(int i = 10)
{
	cout << i << endl;
}

int main()
{
	test(1);
	test();
	return 0;
}

函数变量在写的时候,类似初始化。这样写有什么用途呢?首先看看没传参数和传参数时打印的结果是什么:
在这里插入图片描述

这说明了一个啥道理呢?在你传参数后,你传什么参数就打印什么参数,但是你需要传参时却没有传参,它打印的就是一个函数那里定义的值:10

这个10可以理解成一个备胎,在不需要你的时候嘞,你就是个空气。又因为函数test必须要有一个参数,所以在你没有传参的时候,这个备胎就可以排上用场了。

上面函数的备胎只有一个,但是备胎也可以有多个,海王嘛,多养了点鱼。

void test2(int a = 10, int b = 20, int c = 30)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

这时候我们在使用test2这个函数时也可以多样化传参:

void test2(int a = 10, int b = 20, int c = 30)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

int main()
{
	test2(1,2,3);
	test2(1, 2);
	test2(1);
	test2();
}

看看结果:
在这里插入图片描述

3个备胎,随便用哪个都是可以的。但是有一点要注意:使用缺省值,必须从右往左连续使用。意思就是不能向下面这样跳着传参:

	test2(, 2, );
	test2(, , 3);

而且在传参时顺序和函数那一块形参的顺序是相互对应的。就是说test2(1, 2)传给的是a,b两个参数而不是a,c两个参数。

2.2半缺省

和使用缺省值一样,函数那部分,也可以不用把所有备胎带在身上。比如这样写:

void test3(int a, int b = 20, int c = 30)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

void test4(int a, int b, int c = 30)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

和使用缺省值一样,半缺省也是要从右到左连续缺省。你不能这样写:

void test3(int a = 10, int b, int c = 30)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

void test4(int a = 10, int b = 20, int c)
{
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << endl;
}

2.3缺省值补充

函数写缺省值的时候,声明和定义推荐在声明的时候写,定义的时候不写。
如果向下面这样声明,定义都写了缺省值:
在这里插入图片描述
会导致编译出错。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值