目录
前言
终于开始更新辣
这是第一篇C++文章,当然数据结构也还会更新捏
首先介绍一下C++:
C++(c plus plus)是一种计算机高级程序设计语言,由C语言扩展升级而产生,最早于1979年由本贾尼·斯特劳斯特卢普在AT&T贝尔工作室研发。
C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。 C++几乎可以创建任何类型的程序:游戏、设备驱动程序、HPC、云、桌面、嵌入式和移动应用等。 甚至用于其他编程语言的库和编译器也使用C++编写。
C++拥有计算机运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。
(资料源于百度百科)
正文
命名空间
我们在使用C++时,创建好源文件后,通常会输入这样的代码
#include <iostream>
using namespace std;
然后再开始编译,而才开始学习的萌新自然会想:iostream是什么?为什么一定要写using namespace std;?有什么用?
其实,iostream是一个类库,意为输入输出流,使用include包含这个文件后,才能使用cin cout等函数,也就是说,iostream是一个C++标准库的头文件。
那么using namespace std呢?要了解这个,我们就要学习命名空间的作用和用法
在使用C语言的时候,我们知道,变量是不能同名的,会造成命名冲突,同时C语言还规定,变量名不能与关键字冲突,但是并没有规定变量名不能与库中的变量名冲突,这就导致了我们在编译的时候会出现错误,例如:
#include<iostream>
#include<stdlib.h>
using namespace std;
int main()
{
int rand = 1;
printf("&p\n", rand);
printf("&p\n", rand);
return 0;
}
在stdlib头文件中,rand被定义为一个函数,但是我们已经将rand定义为一个局部变量,根据局部优先的原则,这时候运行不会有问题,但是,一旦我们将rand定义为全局变量,由于我们包含了stdlib头文件,头文件展开后也有一个rand函数,这时rand的定义就会产生歧义
显然我们不能记住所有的头文件中包含的函数,以此规避变量名的重复使用,为了避免这个问题,C++用了命名空间来解决,顾名思义,我们可以把命名空间当做一块新开的空间来使用,在这个空间,你可以定义任何变量和函数,当然,你也得遵循规则,不在同一个命名空间中定义相同名字的变量
当我们使用命名空间定义好变量后,他就不会与其他空间的变量或函数产生冲突了
关于命名空间的定义方法:
namespace Wonder
{
int rand = 0;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
namespace Sun
{
int rand = 1;
}//命名空间套娃
}
我们可以看到,在我们定义的命名空间Wonder中,我们可以定义变量,函数或是结构体,甚至我们可以进行命名空间套娃,那我们定义好了这样一个空间,我们要如何使用它呢?这里就要介绍一下域作用限定符 “::” 域作用限定符用来指定查找命名空间的变量,既然有了明确指向,那么编译器在处理相同名字的变量或函数时,就不会产生歧义
#include<iostream>
#include<stdlib.h>
using namespace std;
namespace Wonder
{
int rand = 0;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
namespace Sun
{
int rand = 1;
}
}
int main()
{
printf("&p\n", rand);
printf("&p\n", Wonder::Sun::rand);
printf("&p\n", Wonder::rand);
//printf("%d\n", Add(1, 2));//error
printf("%d\n", Wonder::Add(1, 2));
struct Wonder::Node node;
return 0;
}
在上面的代码中,我们可以看到,我们定义的rand并没有引发冲突,同时,定义在Wonder中的变量,不能够直接调用,我们可以理解为这些变量和函数被Wonder这个命名空间封装了起来,想要使用它们就必须进行授权展开,而使用域作用限定符就是有指向性的到Wonder这个空间寻找对应的变量
当然,我们也可以用using namespace Wonder;直接授权展开,或者用using Wonder::Add;部分授权展开,但是为什么不这么做?
因为直接授权展开将变量都显示出来后,会造成指向不明确,与定义一个全局变量一样,会发生歧义,而部分授权展开,如果当前有函数也定义为Add,就会有重名的问题,所以本质上问题都一样
那为什么我们在开头就要用using namespace std?因为std是C++标准库的命名空间,c++相关语法在iostream中被封装在命名空间std中,直接展开便于我们使用相关的函数,大家可以自己尝试将using namespace std这行代码删掉后再去编译,会发现函数都用不了了,或者说只能通过域作用限定符进行操作使用,但是这样显然非常麻烦
到这里,相信你对命名空间已经有了一个基本的认知,命名空间还有几个小特性,比如:命名空间名字相同时会合并为一个命名空间,就算在两个文件中各有一个命名空间名字一样,在使用时也可以正常使用
缺省函数
先看一段代码
void Fun(int a = 1)
{
cout << a << endl;
}
int main()
{
Fun(2);//a = 2
Fun();
return 0;
}
运行后我们发现,第一个Fun函数调用得到的结果是2,第二个调用得到的结果是1,这就是一个缺省函数,int a = 1这个参数叫做缺省参数,为什么要叫缺省呢?C++中规定,这类函数在使用时,实参个数可以与形参不相同。如果没有传参,就会自动调用定义时给的参数,如果传参了,就会调用传过去的参数,这相当于不给传参的时候他是缺省的,但是缺省时他会自动使用定义时给的int a = 1,这个int a = 1也叫缺省参数,也就是默认值
void Func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
void Funa(int a, int b = 10, int c = 20)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int main()
{
Func(1, 2, 3);
Func(1, 2);
Func(1);
Func();
//Func(, 1, );error
return 0;
}
全缺省函数,即这个函数所有的参数都是缺省参数,也就是都给了默认值,那么在调用的时候,我们就可以传一个参数,两个参数或者三个参数,当然,这时候可能有萌新会问:那我可不可以只传第二个参数,不传第一和第三个参数?答案是不能,C++规定,从左往右显示传参,否则就会报错,同时,在定义缺省函数的时候,需要我们从右往左定义缺省参数,同理,半缺省函数在调用时,没有缺省值的地方必须传参
缺省函数还有一个重要的点:在头文件中,不允许声明和定义同时给缺省参数,只能声明给定义不给。为什么呢?因为如果头文件中定义的缺省参数和源文件中定义的缺省参数不同,就会引发错误
顺便一提,在.h里面定义的函数如果不进行声明和定义分离,会出现链接错误,因为如果有多个cpp引用这个头文件,这个函数会造成重定义错误,因为在不同的包含了该头文件的cpp中都有这个同名函数,所以,在有多个h和cpp文件的时候,在头文件定义函数时要做到声明和定义分离,在cpp中用相应的命名空间调用使用,当然,也可以用static静态来解决,它相当于把外部链接改为了内部链接,让这个函数只能在当前文件使用。
了解完缺省函数后,可能大部分人会觉得这玩意儿好像没啥用,但其实他的用处很广泛,比如,我们原来用C语言写的栈,现在就可以用缺省函数改进
namespace Wonder
{
typedef struct Stack
{
int* a;
int top;
int capacity;
}ST;
void StackInit(ST* ps, int n = 4)
{
ps->a = (int*)malloc(sizeof(int) * n);
ps->top = 0;
ps->capacity = 0;
}
void StackPush(ST* ps, int x)
{
//..
}
}
int main()
{
Wonder::ST st2;
StackInit(&st1, 10);
for (int i = 0; i < 10; i++)
{
StackPush(&st, i);
}
Wonder::ST st2;
StackInit(&st2, 100);
for (int i = 0; i < 10; i++)
{
StackPush(&st, i);
}
Wonder::ST st2;
StackInit(&st3);
for (int i = 0; i < 10; i++)
{
StackPush(&st, i);
}
return 0;
}
我们随便定义一个栈,可以看到,如果在这里用C语言的方法,使用宏定义一个N,进行数值操作变换他的个数,在我们需要用到多个栈的时候就不适用了,比如,我的st1需要你开辟5个空间,N的值定为5,可是我st2又需要开辟10个空间,st3需要20个空间,那N的值该取多少?20吗?那显然st1和st2都会浪费空间,这时候,缺省函数的作用就体现出来了,我们给初始化函数一个缺省值4,如果我们不传参数,也就是不确定需要几个空间,那么初始化函数就会自动先帮你开辟4个空间,如果你确定了需要几个空间,那就传参过去,初始化函数就会使用你传的参数,进行空间的开辟
函数重载
函数重载是C++中一个比较重要的东西,C++允许了我们定义重载函数,重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数完成不同的功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。不能只有函数返回值类型不同。
(资料来自百度百科)
重载函数要求同名不同参,在C语言中不允许同名函数,但C++允许,但必须构成重载函数,也就是说,如果同名同参,不构成重载函数,也就不能使用,同时,不同命名空间中的相同名字的函数不构成函数重载,因为作用域不同,相同的命名空间可以构成函数重载
int Add(int left, int right)
{
cout << "int Add" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add" << endl;
return left + right;
}
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.1, 1.2) << endl;
//函数重载可以自动匹配
return 0;
}
上述代码就简单的举了一个重载函数的例子,在调用时,重载函数可以自动匹配到对应的函数
缺省函数可以和重载结合,这两者没有关系,上面两个函数也构成重载,因为参数个数不同,但是,这样结合在调用时容易引起歧义
void fuc(int a)
{
cout << "fuc(int a)" << endl;
}
void fuc(int a, int b = 1)
{
cout << "fuc(int a, int b)" << endl;
}
这里我们定义了两个重载函数,同时下面的fuc函数也是半缺省函数,从语法上来看是没有问题的,也可以正常运行,但是,当我们调用fuc(1)时,就会出现问题,因为你只传了一个参数,编译器会搞不清楚你想要调用的是第一个函数还是第二个函数
结尾
本篇文章讲的内容十分初级,理解起来并不困难,这也是我们刚接触C++最需要理解学习的东西,也对我们后面的学习很有帮助,希望观众姥爷多多支持捏