一.C语言与C++之间的联系
1.C语言是结构化和模块化的语言,适合处理较小规模的程序,对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。我们的C++祖师爷Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一 种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而 产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的 程序设计,还可以进行面向对象的程序设计。
二.命名空间
在C语言中,解决不了命名冲突的问题,什么是命名冲突呢?就比如下面一段代码
#include<stdio.h>
#include<stdlib.h>
int rand = 0;
int main()
{
printf("%d \n", rand);
return 0;
}
//error C2365: “rand”: 重定义;以前的定义是“函数”
当此代码运行时就会报错:“rand”: 重定义;以前的定义是“函数”,这是因为,stdlib.h这个文件中包含了以后rand()的函数,而编译器默认查找是先在局部域中,让后再到全局域中查找,所以,在编译链接的时候,头文件会展开,头文件里面的rand就跟我们全局的rand冲突了,这就是命名冲突.这就是C语言的问题之一.那么谁跟谁会产生命名冲突呢,形象的说,就是(我们和库,我们之间).
2.1命名空间定义
这时我们C++祖师爷为了解决这个问题,创建namespace关键字.定义命名空间,后面跟命名空间的名字,然后接一对{};里面即为命名空间的成员,这个空间即为命名空间域.
现在我们如何来解决命名空间冲突问题呢,我们接着看下下面一段代码:
#include<stdio.h>
#include<stdlib.h>
namespace bit
{
int rand = 0;
}
int main()
{
//因为编译器默认访问局部域,全局域,
//所以会打印头文件中rand的地址
printf("%p \n", rand);
//因为编译器默认访问局部域,全局域,
//所以如果要访问命名我们就先要(名字+::)
//所以就会打印命名空间域中的rand
printf("%d \n", bit::rand);
return 0;
}
我们可以把冲突的变量放入命名空间域中,这样就可以有效避免命名冲突,此外bit是命名空间的名字,一般开发中是用项目名字做命名空间名。
难道命名空间中只能存放变量吗,其实我们还可以定义函数/结构体等,同时也可以进行嵌套。如下:
namespace bit
{
int rand = 0;
int add(int a, int b)
{
return a + b;
}
struct Node
{
struct Nofe* next;
int val;
};
namespace si
{
int sub(int a, int b)
{
return a - b;
}
}
}
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
2.2命名空间的使用
那我们命名空间如何正确使用呢?
1>加命名空间名称及作用域限定符
1.在上述代码中如果要使用命名空间域里面的rand
正确的使用方式:bit::rand (域名+::+变量);
2.如果要使用add函数
正确的使用方式:bit::add(1,2) (域名+::+函数名(参数,参数));
3如果要使用结构体struct Node
正确的使用方式:struct bit::Node pa (域名+::+结构体名 + 变量名);
这里注意的是 bit::的位置,是在struct与Node之间;
4.当命名空间嵌套时,使用sub函数
正确的使用方式:bit::si::sub(2,1) (域名+::+嵌套域名+::+函数名(参数,参数));
printf("%d \n", bit::rand);
printf("%d \n", bit::add(1, 2));
printf("%d \n", bit::si::sub(2, 1));
struct bit::Node uat;
2>使用using namespace 命名空间名称引入
----这时我们引用关键字using,使用using namespace bit可以对命名空间域进行展开,那么编译器默认查找就变成:a.局部域 b.全局域 c.展开命名空间域,这时候就不需要加命名空间名称及作用域限定符就可以使用其中的变量/函数/结构体,
#include<stdio.h>
#include<stdlib.h>
namespace bit
{
int rand = 0;
int add(int a, int b)
{
return a + b;
}
struct Node
{
struct Nofe* next;
int val;
};
namespace si
{
int sub(int a, int b)
{
return a - b;
}
}
}
using namespace bit;
using namespace si;
int main()
{
printf("%d \n",rand);
printf("%d \n",add(1, 2));
printf("%d \n",sub(2, 1));
struct Node uat;
return 0;
}
这时如果要使用sub函数在展开命名空间bit的同时也需要展开命名空间si,using namespace si,
1.展开命名空间的坏处
1.全局域与展开命名空间域
如果认真看上述代码其实就可以看出有一个bug,还记得刚才我们提到的如果使用展开命名空间,编译器的默认查找吗,a.局部域 b.全局域 c.展开命名空间域,我们在上述代码中包含了头文件stdlib.h,他是处在全局域中的,那么编译器,默认先是在局部域,然后在全局域,最后在命名空间域中查找,所以,在上述代码中如果要打印rand,这时候就会产生命名冲突,就会报错。
2展开命名空间域与展开命名空间域
当两个命名空间域中有相同名时,对两个命名空间都进行展开,此时就会发生命名冲突,如下
#include<stdio.h>
namespace bit
{
int add = 0;
}
namespace si
{
int add = 0;
}
using namespace bit;
using namespace si;
int main()
{
printf("%d \n",add);
return 0;
}
像代码这种,就是命名空间冲突,
所以展开命名空间有一定的缺点,在项目中,稍不注意就可能造成命名冲突,所以命名空间尽可能不要随意展开。
3>使用using将命名空间中某个成员引入
根据上述结果,我们不能随意展开命名空间,因此我们引进了使用using将命名空间中某个成员引入,比如:命名空间有a与b两个变量,其中a使用的次数多,b使用的次数少,此时我们就可以指定展开a,就是指定展开某一个。如下:
#include<stdio.h>
namespace bit
{
int a = 0;
int b = 1;
}
using bit::a;
int main()
{
printf("%d \n", a);
printf("%d \n", a);
printf("%d \n", a);
printf("%d \n", a);
printf("%d \n", a);
printf("%d \n", bit::b);
return 0;
}
如果经常使用某一成员就可以对命名空间指定成员进行展开。
3.总结:
1.命名空间定义的是一个域;
2域可以做到名字的隔离;
2.不同域可以定义同名的变量/函数/类型;
三.C++输入&输出
---输入输出所需知识介绍
1.在C++中输出用cout对应C语言中的printf,输出用cin对应的是scanf,
2.cout标准输出对象(控制台),cin标准输入对象(键盘),
3.使用cout与cin必须包含头文件-<iostream>,还需要使用命名空间std,std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中,
4.在C++中如果要换行则可以用endl,也可以使用'\n',
5.<<是流插入运算符,>>是流提取运算符,
6.另外C++中输入输出可以自动识别类型,不需要手动控制
----接下来我们通过代码来体验C++的输入输出的具体实现:
#include<iostream>
int main()
{
int a = 2;
double b = 1.11;
std::cout << a <<' ' << b << '\n' << std::endl;
std::cin >> a >> b ;
std::cout << a << ' ' << b << '\n' << std::endl;
return 0;
}
如果要多次使用cout,endl,cin其中一个,现在我们就可以使用刚才所讲到的,对命名空间某一成员进行展开,
using std::cout;
using std::endl;
using std::cin;
如果都要经常使用也可以使用using namespace std;对命名空间全部展开,但是不建议这样,刚才也说明原因。
---std命名空间的使用惯例:
std是C++标准库的命名空间,如何展开std使用更合理呢?
1. 在日常练习中,建议直接using namespace std即可,这样就很方便。
2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对 象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模 大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。
四.缺省参数
---缺省参数的使用及说明
缺省参数就是在声明或定义函数时,给函数参数指定的缺省值(说通俗一点就是参数的默认值),如果函数传参时,没有传实参,则使用该函数的缺省值,反之则使用改实参
解下来就是简单代码实现能够更好展现:
#include<iostream>
using namespace std;
void fun(int c = 2)
{
cout << c << endl;
}
int main()
{
fun();
fun(10);
return 0;
}
从运行结果可以看见,如果不传参数,则使用缺省值,反之则是该参数。
---缺省参数的分类
1.全缺省参数
顾名思义,全缺省参数就是给函数所有参数都给定缺省值。
代码如下:
#include<iostream>
using namespace std;
void fun(int a = 2,int b= 3,int c= 3)
{
cout<<a<<" " << b<<" " << c << endl;
}
int main()
{
fun();
fun(10,11,12);
return 0;
}
2半缺省参数
这里重点是说一下半缺省参数,顾名思义,是指有的参数有缺省值,有的没有缺省值,并不是对半分,
现在我们要分析的是半缺省参数是从左边到右边缺省,还是从右边往缺省,还是中间缺省,
1.当从左边往右边缺省时:
void fun(int a=1, int b = 3, int c)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
fun(10, 11);
return 0;
}
这里传参就会产生歧义,fun(10,11),这里他就不知道要传给谁,同理中间缺省也行不通。
2从右边往左边缺省时:
void fun(int a, int b = 3, int c = 4)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
fun(10, 11);
return 0;
}
这里就不会产生歧义,他就会先传a,再传b,此时c没有传,所以使用是缺省值。
注意:这里要注意的是C++规定传参不能跳跃传,必须顺序传。
----总结
1. 半缺省参数必须从右往左依次来给出,不能间隔着给;
2. 缺省参数不能在函数声明和定义中同时出现,如果同时出现就会不确定缺省值到底是谁,在项目中,一般都是在声明处定义缺省参数;
3.缺省值必须是常量或者全局变量,上面代码都是展示的是常量,下面代码则是全局变量的例子:
int i = 1;
void fun(int a=i, int b = 3, int c = 4)
{
cout << a << " " << b << " " << c << endl;
}
int main()
{
fun();
fun(10,11,12);
return 0;
}
这里的 i 则为全局变量,根据代码结构,说明缺省值可以是全局变量。