文章目录
- 前言
- 一、C++关键字(C++98)
- 二、命名空间
- 命名空间的定义
- 命名空间的使用
- 三、C++输入 & 输出
- 四、缺省参数
- 总结
前言
其实有时候我也会尝试代入下祖师爷本杰明当年在贝尔实验室的心理活动,我心想,他可能一开始是大抵受不了C语言的某些缺点,自己添加一些特性,久而久之,就有了Cpp,所以,我们一开始感慨Cpp较之于C的方便,这点大家阅完全篇就能马上得到反馈
可是,加着加着,有些特性也就复杂了起来,这点你到后期会慢慢感悟的
哈哈,所以说事物的发展不说是事与愿违,但往往是出人意料的,这才是常态~
正文开始!
一、C++关键字(C++98)
先容许我不对关键字进行具体的讲解,后面我们学到再细讲
C++中总计有63个关键字:
可别觉得多,其中32个关键字我们在C语言就已经学过了:
其实,我在开篇引言也说了,把Cpp当作是C的超集,是没有多大问题的
因为Cpp包含了C的绝大部分语法
举个例子,< iostream > 这个头文件兼具 < stdio.h > 这个头文件的作用
二、命名空间
命名空间的定义
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
举个例子,请看一个错的C程序:
#include<stdio.h>
#include<stdlib.h>
int rand = 0;
int main()
{
printf("%d",rand); // err
return 0;
}
报错给出的理由是重定义,其实 rand 是 stdlib.h 声明的库函数,在预编译阶段头文件会展开,会导致rand重命名,不得不说的是,这很麻烦,至少对于C语言来说
未来我们在工作的时候,如果遇到一个大项目,每个人都负责自己的模块,到最后合并的时候,发现名字冲突,就很尴尬,且没有很好的方法
这时候,只能下班两个人上天台决斗了,谁输谁改名!
哈哈,戏言戏言~
基于此,Cpp中给出了 命名空间 这个解决办法,其定义如下:
// 定义命名空间格式
namespace 命名空间名字
{
命名空间成员
}
关于命名空间的定义,本质上是定义一个命名空间域,命名空间中的所有内容都局限于该命名空间中:
1. 命名空间的正常定义:
namespace tan
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
2. 命名空间的嵌套定义:
namespace N1
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
namespace N2
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
- 另外,同一个工程中允许存在多个相同名称的命名空间,编译器最后会将其成员合成在同一个命名空间中
所以,我们不能在相同名称的命名空间中定义两个相同名称的成员
命名空间的使用
了解了其定义,我们再来思考下怎么使用,先来了解下编译器的搜索原则
编译的时候,默认的查找顺序是局部 -> 全局,不会到命名空间里面找,至少默认情况下是这样的,除非你把命名空间给展开了,那就再加个-> 展开的命名空间域
打个比方,假如一农村小孩,家里没葱,首先是去自家的菜地里面去找;若找不到,则在村里的野空地去找;而展开的命名空间域就可以理解为张大爷人心善,在自家的菜地里面插了一块牌子写道“菜量有余,需要自取”
所以,命名空间解决名字冲突的核心逻辑就是将两个同名的变量/函数/类型放到两个不同的域里面
如下,原本两个a在同一个域(全局域)里面,必然发生重名冲突,可是两个命名空间tan1、tan2分别把它们包在了各自的命名空间域里面,就避免了冲突,可以观察到下列代码是正常退出的:
那么,在需要的时候,如何从命名空间中找的需要的变量呢?这就不得不说到域限定符 :: 了,:: 操作符左侧是特定的域,右侧是变量,来个具体的例子:
#include<iostream>
int a = 10;
int main()
{
int a = 1;
printf("%d\n", a); // 在默认情况下,优先使用全局变量a = 10;
printf("%d\n", ::a); // 在域作用符左侧不写任何域,表示在全局域中找变量a = 1;
return 0;
}
哈哈,这是第一种命名空间的使用方式,即加命名空间名称及作用域限定符,不加命名空间名称则视为从全局域开始查找
第二种命名空间的使用方式是使用using将命名空间中某个成员引入,我们来看以下代码:
#include<iostream>
namespace tan1
{
int a = 1;
}
namespace tan2
{
int a = 2;
}
using tan1::a;
int main()
{
printf("%d\n", a); // 1
return 0;
}
其实就相当于展开了一个命名空间的某一成员,对于某些成员需要频繁使用的前提下很方便,但是我们也要认识到一味地展开也并非是有益的,就比如上述代码假如我们也把tan2中的a引入,即using tan2::a;,那你说,主函数里面打印 a 到底该打印哪个 a ?
自己田没有菜了,村里野地也没有,这时候张大爷和王阿姨都插了一块牌子说自家葱可按需自取,这时候你就会纠结到底该摘谁家的了,就是这个道理~
第三种使用方法就是干脆整个命名空间给全部展开,注意这种方式在项目上还是比较少见的,毕竟命名空间最初产生的原因就是因为项目重名嘛
代码如下:
namespace tan1
{
int Add(int a, int b) { return a + b; }
int a = 1;
}
namespace tan2
{
int a = 2;
}
using namespace tan1;
int main()
{
int ret = Add(1, 2); // tan1命名空间域已经全部展开,Add作为成员之一可以被访问
// 当然,加个访问符tan1::Add(1,2);也没问题
printf("%d\n", ret);
return 0;
}
总而言之就是,要对编译器的搜索顺序有个了解,命名空间就相当于是人为定义了一个域,要想访问域里面的是数据,可以展开部分成员或整个空间,或者通过域作用限定符 :: 来访问
三、C++输入 & 输出
来个经典 “Hello,world!” 起手吧,毕竟这是我们就是由它迈入编程的大门
#include <iostream>
using namespace std;
int main()
{
cout << "Hello,world!" << endl;
return 0;
}
Hello,world!
较之于C,Cpp采用cout、cin分别用来输出输入,这就是Cpp向这个美好的世界问好的方式
关于上述代码,我想讲述几点你需要知道的知识:
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std (即按前文所述,iostream把一些函数全部包在了一个叫做标准库的命名空间(std),你需要通过域限定符来访问,如std::cin、std::cout,或者直接展开std标准库)
- cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。
- <<是流插入运算符,>>是流提取运算符
- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型 (但是有时候我们也确实需要限制变量的形式,这很自由的)
- 关于cin、cout、<<、>>后面我们会有更详细的介绍,这涉及ostream、istream、运算符重载等知识,做好准备吧~
为了验证我说C++输入输出很方便可以自动识别非虚,你可以自行尝试运行以下代码
// 我一直强调这很自由,全凭你个人方便
#include <iostream>
using namespace std;
int main()
{
int i;
double d;
char arr[20];
cin >> i;//读取一个整型
cin >> d;//读取一个浮点型
cin >> arr;//读取一个字符串
cout << i << endl;//打印整型i
cout << d << endl;//打印浮点型d
cout << arr << endl;//打印字符串arr
return 0;
}
四、缺省参数
这部分内容和接下来的函数重载,都是提升编程开发效率的利器
缺省参数的概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参
我们立马来个例子:
#include <iostream>
using namespace std;
void Print(int a = 0)
{
cout << a << endl;
}
int main()
{
Print(); // 没有指定实参,使用参数的默认值(打印0)
Print(10); // 指定了实参,使用指定的实参(打印10)
return 0;
}
缺省参数的两种分类
- 全缺省参数,即函数的全部形参都设置为缺省参数
void Print(int a = 10, int b = 20, int c = 30)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
- 半缺省参数,即函数的参数不全为缺省参数
void Print(int a, int b, int c = 30)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
缺省参数的正确使用
- 缺省参数不能在函数声明和定义同时出现
说明:从两个方面来说,一方面如果声明与定义位置同时出现,恰好两个位置提供的值不同,那么编译器就无法确定到底该使用哪个缺省值,另一个方面来说就算两个位置提供的值相同,祖师爷规定也不能这种方式操作,实际上,我们一般放在声明 - 半缺省实(或形)参数必须从右往左依次来给出,不允许间隔着给
说明:在语法层次上,我们属于学习者是根据语法规定来进行学习。如果想要更有说服力的答案,其实就是防止歧义,明确实参该传给那个形参,主要是给无缺省参数的参数 - 缺省值必须是常量或者全局变量,如下:
//正确示例
int x = 30;//全局变量
void Print(int a, int b = 20, int c = x)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
缺省参数的一个具体实现场景
我们在学习栈这个数据结构的时候,有时候明确初始化该给多少空间,有时候则不明确,这时候就可以用缺省参数来完成初始化
//1.确定要插入100个数据
StackInit(&st1, 100);
//1.确定要插入10个数据
StackInit(&st2, 10);
// 3、不知道要插入多少个
StackInit(&st3);
当然,我们后期会慢慢感受到缺省的便利之处
总结
介绍了命名空间、输入输出、缺省参数这几个小语法
不急,下篇将会为大家带来两个更重要的函数重载与引用等知识点
敬请期待~