目录
- 从C到C++
- 命名空间
- 同名冲突
- 命名空间的定义
- 命名空间的使用
- C++的输入和输出
- 缺省参数(默认参数)
从C到C++
C语言的出现是计算机科学和工程史上的一个重要里程碑,许多现代计算机语言都受C语言的影响。C语言是面向过程的,结构化和模块化的语言。 C语言的设计者必须细致地设计程序中的每一个细节,准确考虑程序运行时每一个步骤发生的事,但是随着计算机应用的推广,需要解决更多更复杂的问题,C语言就太适合了。
于是乎,20世纪80年代,计算机界提出了面向对象的程序设计OOP(Object Orientde Programming)思想,由此支持面向对象的程序设计语言应运而生。
1982年,AT&T Bell(贝尔)实验室的Bjarne Stroustrup博士及其同事在C语言的基础上引入并扩充了面向对象的概念,为了表达该语言与C语言的渊源关系,将其命名为C++。
C++保留了C语言原有的主要优点,增加了面向对象的机制。 又因为C++是C发展而来的,与C兼容,用C语言编写的程序基本上可以不加修改的用于C++。C++是C的超集,可用于面向过程的结构化设计,但更多地用于面向对象的程序设计,是一种功能强大的混合型的程序设计语言 。
下面就来走进C++,敲开C++的大门。
命名空间
同名冲突
在C/C++中,变量,函数和C++中要学习到的类都是大量存在的。由于它们的大量存在,我们就喜欢将它们定义在全局作用域上,以方便修改。
在实际生活中,通常会将一个项目分到几个小组去写,最后整合,这样时间效率都会增高。但是这就会出现一个问题,在整合项目的时候,可能会有些命名上的重复,导致程序报错。
不仅如此,在程序设计中往往需要引用一些库(包括C++编译系统提供的库,以及开发者字节开发的库等等),为此需要包含有关头文件。如果在这些库中包含有与程序的全局实体同名的实体,或者不同库有相同的变量或函数或类的名字,就会在编译时出现名字冲突,也就是全局命名空间污染。 那么如何解决这个问题,C++中namespace 关键字的存在就是针对这种问题的。
我们已经说了,写程序时容易发生命名冲突,则使用命名空间的目的就是对标识符的名称本地化,以避免命名冲突或文字污染
。命名空间实际上就是一个由程序设计者命名的内存区域。所以一开始在学C语言的时候,我们就知道了有两个作用域,一个是全局域,另一个是局部域,现在加了这个命名空间以后,C++的一个程序上就会有三个域的存在
:
- 全局域
- 局部域
- 命名空间域
例如:
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
rand本身是一个库函数,用来生成随机值,要用的时候需要包含在头文件stdlib.h中。
而此时全局变量中又定义了一个rand,导致printf在输出时不知道应该调用库函数里面的rand,还是全局变量中的rand,此时就发生了命名冲突。
命名空间的定义
定义命名空间,需要使用到namespace关键字,后面接着命名空间的名字,最后加一对{}即可,{}中即为命名空间的成员。命名空间中不止可以包括变量,还可以包括函数,结构体,类(后面会学习到),模板,命名空间(嵌套使用也可以)等,例如下面的例子:
namespace ns1
{
int a = 1;//定义变量
int Add(int x, int y)//定义函数
{
return x+y;
}
struct ListNode//定义结构体
{
int val;
struct ListNode* next;
};
namespace ns2//嵌套了另一个命名空间
{
int sub(int x, int y)
{
return x+y;
}
}
}
还有值得要提的一点是,同一个工程中存在多个相同名称的命名空间,编译器最后都将会合成在同一个命名空间中。 就像你在北京有套房,在上海也有套房,那别人也就知道你是有两套房,都在你的名下。
如:
namespace ns1
{
int a = 1;//定义变量
int b = 2;
}
namespace ns1
{
int Add(int x, int y)//定义函数
{
return x + y;
}
}
最终编译器会认为是:
namespace ns1
{
int a = 1;//定义变量
int b = 2;
int Add(int x, int y)//定义函数
{
return x + y;
}
}
命名空间所定义的命名空间域,将命名空间中的内容都局限于该命名空间中。
命名空间的使用
我们介绍了如何去定义命名空间,下面来看看它是如何使用的。
命名空间有三种使用方式:
- 第一种方式:加命名空间名称及作用域限定符(在需要使用命名空间中的内容时再制定访问)
#include<stdio.h>
namespace ns1
{
int a = 1;//定义变量
int b = 2;
}
int main()
{
// printf("%d\n", a);
// 这种直接想打印命名空间域里面的变量时编译器会报错
// error C2065: “a”: 未声明的标识符
printf("%d\n",ns1::a);
return 0;
}
我们发现在命名空间名字的后面有一个::符号,这叫做域作用限定符, 也就限定了内容在哪块查找。如:
#include<stdio.h>
int a = 10;
int main()
{
int a = 20;
printf("%d\n", a); //打印的是局部域中的a是20
printf("%d\n", ::a); //打印的是全局域中的a是10
return 0;
}
- 第二种方式:使用using将命名空间中某个成员引入
即在程序中需要大量使用命名空间中的某一个成员时,每一次都去加上限定符比较麻烦,于是我们用using来制定展开某一个,例如:
#include<stdio.h>
namespace ns1
{
int a = 1;//定义变量
int b = 2;
}
using ns1::a;
int main()
{
printf("%d\n", a);
printf("%d\n", a);//此时我们使用a时就不用再加上限定符
printf("%d\n", ns1::b);//我们只是展开了变量a,所以在使用b的时候还是要加上限定符
return 0;
}
- 第三种方式:使用using namespace命名空间名称引入
使用了这个命名空间引入,可以将命名空间中的变量所有都展开来,后面在使用时都不需要加上限定符。例如:
#include<stdio.h>
namespace ns1
{
int a = 1;//定义变量
int b = 2;
}
using namespace ns1;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
注意!
:编译器在编译时对变量等的查找会有默认查找顺序,首先查找的是当前局部域,其次是全局域。
只有命名空间被展开了,最后才会到这里面去找。
C++的输入和输出
C语言的输入输出是有scanf和printf来控制的,而在C++中则是由cin和cout来控制。
例如:
#include<iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
cout << "a = " << a << " " << "b = " << b << endl;
return 0;
}
结果如下:
我们发现该程序进行了对a和b的输入并且打印输出操作。
在C++中,cout和cin是系统定义的对象名,cin是标准输入对象(键盘),cout是标准输出对象(控制台),(我们运行结果出来的框框就是控制台)。
<<是流插入运算符,与cin配套使用,>>是流提取运算符,与cout配套使用。
在C语言中,scanf和printf的使用需要包含头文件<stdio.h>,C++中也一样,在使用cin和cout时,需要包含头文件 < iostream>。
C++标准库中的类和函数时再命名空间std中声明的,如果需要使用C++标准库中的内容,就需要使用using namespace std来声明,表示要用命名空间std中的库函数
。
使用C++的输入输出更加方便,不需要像printf/scanf那样,需要手动控制格式C++的输入输出可以自动识别变量类型。
注意:
std是C++标准库的命名空间,应该如何去使用
- 在日常练习中,可以直接使用using namespace std 即可,方便使用各种库函数。
- 因为using namespace std展开,标准库就全部露出来了,如果我们定义根库重名的类型/对象/函数,就存在冲突问题,这种问题在日常练习中很少出现,但是项目开发中代码较多,规模较大,就很容易出现问题,所以在项目开发中建议使用:像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。
缺省参数(默认参数)
缺省参数是声明或定义函数时为函数的参数指定一个缺省值(默认值)。在调用该函数时,如果没有指定实参则采用该形参的缺省值(默认值),否则使用指定的实参。
缺省参数的分类
- 全缺省参数(全默认参数)
也就是函数的形参全部给了默认值
#include<iostream>
using namespace std;
void func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a;
cout << " b = " << b;
cout << " c = " << c << endl;
}
int main()
{
func(1, 2, 3);
func(1, 2);
func(1);
func();
return 0;
}
运行结果如下:
- 半缺省参数
#include<iostream>
using namespace std;
void func(int a , int b = 20, int c = 30)
{
cout << "a = " << a;
cout << " b = " << b;
cout << " c = " << c << endl;
}
int main()
{
func(1, 2, 3);
func(1, 2);
func(1);
return 0;
}
运行结果如下:
在使用缺省参数时有几个注意事项如下:
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数的声明和定义中同时出现
- 缺省值必须是常量或者是全局变量
- C语言不支持。
对于第2点的解释:
我们对一个函数进行声明和定义时,通常把声明放在.h文件中,定义放在.cpp文件中。
所以会出现:
xxx.h文件中函数声明:void func(int a = 10);
xxx.cpp文件中函数定义:void func(int a = 20){…}
此时两个位置提供的值不同,编译器就不知道该用那个缺省(默认)值,会报错。
今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。