1.命名空间
在c/c++中,变量、函数和后面要学的类都是大量的存在,这些变量、函数和类的名称将都存在于全局作用域中,可能会会导致很多冲突,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是为了针对这种问题。
而定义一个命名空间需要用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
看下面的例子:
//1.常见的命名空间
namespace N1
{
//命名空间中的内容是可以定义变量也可以定义函数
int Add(int a, int b)
{
return a+b;
}
int a;
int b;
}
//2.命名空间还可以进行嵌套
namespace N2
{
int a;
int Sub(int c, int d)
{
return c-d;
}
namespace()
{
int Mul(int c, int d)
{
return c*d;
}
int c;
int d;
}
}
//3. 也可以出现空间名字相同的命名空间
//那么如果一个工程出现多个相同名称的命名空间时是如何处理的?
//其实如果一个工程中出现多个相同名称的命名空间时,编译器最后会合成同一个命名空间
namespace N1
{
int Sub(int a, int b)
{
return a-b;
}
int c;
int d;
}
需要注意的是:一个命名空间定义了一个新的作用域,命名空间中所有内容都局限于这个命名空间中。
上面我们定义了几个命名空间,那么我们如何使用这些命名空间中的成员(变量和函数……)呢?
下面我们来看三种命名空间的使用例子:
1.在你使用的成员前面加命名空间的名称和作用限定符:
int main()
{
//printf("%d\n", a); //这样写是错误的,因为命名空间的成员的变量局限于命名空间内
printf("%d\n", N1::a); //这是访问一个普通的命名空间成员的方式
printf("%d\n", N2::N3::a); //这是访问嵌套命名空间中里面的命名空间的成员的访问
return 0;
}
2.使用using将命名空间中成员的引入(这种做法相当于将这个成员的作用域,变成一个全局作用域,那么如果此时出现一个和这个成员名字相同的全局变量时,这时就不能再这样写了,这时只要在变量的前面加一个命名空间的名字和作用域限定符(::)即可);
using N1::b;
//int b;
int main
{
//printf("%d\n", N1::b);
printf("%d\n", b);//这时候变量的前面不用加任何的东西
return 0;
}
3.使用using namespace命名空间名称的引入(这样的作法相当于直接将命名空间的内容导入文件中,相当于全局变量和函数来使用),(同样如果由变量的名字发生冲突时,在变量的前面加命名空间的名字和作用域限定符)
using namespace N1;
int main()
{
printf("%d\n", a);
Add(10,20);
return 0;
}
以上就是命名空间的定义和用法
2.C++中的输入和输出
接下来分析c++的输入和输出与c语言有什么不同?
首先看下面的一个例子:
#include<iostream>
using namespace std;
int main()
{
int i = 0;
cin>>i;
cout<<"hello Word"<<endl;
return 0;
}
1.首先头文件的引用,有的书上iostream后面是有.h的,但是现在基本不加.h,这是为了区分C++和C语言的不同
2.其次为什么要有using namespace std这句话呢?因为C++把标准库存在了标准的命名空间中,而这个命名空间的名字就是std,这步的操作就是将命名空间std中的标准库导入文件中。
3.cout是标准输出,cin是标准的输入,使用这两个来输入和输出更加的方便,因为以前C语言中输出一个变量时都要前面指明变量的数据格式,但是cout不用指出数据的格式。<< 运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项。endl 用于在行末添加一个换行符。
注意:早期标准库将所有的功能都在全局域中实现,声明在.h后缀的头文件中,使用时只要包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确的使用命名空间,规定C++头文件不带.h;旧的编译器(vc)中还支持<iostream.h>的格式,但是后续的编译器已经不支持,因此推荐使用<iostream>+std的方式。
3.缺省参数
1.概念:缺省参数是声明和定义函数时为函数的参数指定一个默认值。再调用该函数时,如果没有指定的实参则采用该默认值,否则使用指定的实参。
如下所示:
void TestFunc(int a = 0)
{
cout<<a<<end1;
}
int main()
{
TestFunc(); //不给函数传参,此时函数使用的是参数的默认值
TestFunc(10); //此时给函数传参,那么函数使用的是传过去的值。
return 0;
}
2.缺省的参数分类
1.全缺省参数(将函数的所有的形参都给一个默认值)
2.半缺省参数(没有将所有的的形参都给默认值)
注意:(1)半缺省的参数必须从右向左依此给出,不能间隔给出。
原因:函数的调用约定(-cdel)规定函数传参从右向左。
(2) 缺省的参数不能在函数声明和定义中同时出现。
原因:如果声明和定义的时候所给形参的缺省值不相同的话,那么如果你不给函数传参的时候,编译器就无法识别到底要使用哪一个缺省值。
(3)缺省值必须是常量或全局变量
(4) C语言不支持
4.函数重载
函数重载是函数的一种特殊的情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参链表(参数的个数或类型或顺序)必须不同,常用来处理实现功能类似的数据类型不同的问题。
如下图所示:
int Add(int a, int b)
{
return a+b;
}
double Add(double a, double b)
{
return a+b;
}
long Add(long a, long b)
{
return a+b;
}
int main()
{
Add(10,20);
Add(10.0, 20.0);
Add(10L, 20L);
return 0;
}
注意:(1)如果两个函数仅仅是函数的返回值不同的话,不会形成函数的重载。
(2)最好不要将一个无形参的函数和一个全缺省函数形成缺省,不然的话会发生问题。
原因如下:
int Add()
{
return 1;
}
int Add(int a = 1, int b = 2)
{
return a+b;
}
int main()
{
Add(); //这个时候如果调用Add函数且不给函数传参时,编译器就不知道调用哪个函数
return 0;
}
6.引用
引用不是定义一个变量,而是给已存在的变量取了一个别名,而且编译器不会为引用的变量开辟一块空间,它和它引用的变量共同使用一块空间。
如下图所示:
void TestRef()
{
int a = 10;
int & ra = a; //这就是引用
printf("%d", &a);
printf("%d", &ra); //这两个的地址是相同的。
return 0;
}
但是引用和引用的实体必须是同一个类型的。
原因如下
double a = 1.34;
int & ra = a; //这种写法是错误的
const int & ra = a; //这种写法可以,但是输出的ra只有a的整数部分。
因为引用和变量的类型是不同的,所以编译器会开辟一块临时的空间来存放int类型的数据,然后将a的整数部分放如临时空间中,但是由于编译器临时开辟的空间的名字和地址我们都不知道,所以这个临时空间不能改变(也就是这块临时的空间具有常性)所以加const以后会通过编译。5.
引用需要注意以下几个特性:
1.引用在定义的时候必须要初始化。
2.一个变量可以有多个引用
3.引用一旦引用一个实体,再不能引用其他实体。
int main()
{
int a = 0;
//int &ra; //这样写是会出现错误的
int &ra = a;
int &rra = a;
printf("%p, %p, %p", &a,&ra,&rra);
return 0;
}
使用的场景
1.做参数:
void Swap(int a, int b) //这样可以直接交换两个变量的数据,并且结果和传变量地址过来的结果是一样的。
{
int tmp = a;
a = b;
b = tmp;
}
2.也可以用作返回值:
int & Test()
{
int a = 10;
return a;
}
但是将引用作为返回值有要注意的地方 你可猜想一下面的代码输出的是什么?
int main()
{
int & a = Test();
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
return 0;
}
为什么是这个结果呢?
这个涉及到了C语言中栈帧的知识,当一个函数调用结束后,空间会被自动释放掉,但是如果你没有做任何操作的话,你任然可以用地址来找到函数中某个变量的值,第一个printf时,存放变量的空间还没有改变,你可以取到正确的值,但是printf执行后可能会修改存放的那片空间,这时第二个printf取值是就无法取到这个变量来的值。第三个也是如此。(这就好比一块写满字的黑板,如果没人修改黑板上的内容的话,你会一直看到原来的内容,但是一旦有人擦掉原来的内容而写上新的东西的话,你看到的就不是原来的内容)。
3.引用和指针的区别
在前面我们说过引用相当于起了一个别名,没有独立的空间,和引用的实体共用同一块空间。
但是从上面的反汇编中可以看出引用在底层实现其实使用空间的,因为引用是按照指针方式来实现。
指针和引用不同:
1.引用在定义时必须初始化,指针没有要求。
2.引用在初始化时引用一个实体后,就不能再引用其他的实体,而指针则可以在任何时候指向同类型的另一个实体
因为int &ra = a; 相当于int * const ra = &a;
3.没有NULL引用,但是有NULL的指针
4.在sizeof中含义不同:引用的结果为引用类型的大小,但指针始终是地址空间所占的个数
5.引用自加即引用的实体增加1,指针自加是向后便宜一个类型的大小
6.有多级指针但是没有多级的引用。
7.访问实体的方式不同,指针需要显示的解引用,引用编译器自己处理。
8.引用使用起来比指针更加的安全
因为指针在函数中需要判空,而引用不需要。
其实引用和指针在传参上的效率几乎相同
5.内联函数
以inline修饰的函数叫做内联函数,编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
inine是一种以空间换时间的做法,省去调用函数额开销,所以代码很长或者循环/递归的函数不适宜使用内敛函数
inline对于编译器来说只是一个建议,编译器会自动优化,但是如果定义为inline的函数体内有循环或者递归的话,编译器会忽略掉内联,所以函数前加inline该函数并不一定会称为内联函数。
如果你要判断一个函数是否是内联函数的话,在调用的语句中,打开反汇编,如果有call语句调用函数的话,代表不是内联函数,反之是内联函数。
c语言中的有宏,C++有什么技术可以替换宏?
1.常量定义 换用const来定义,(因为在c++上一个变量前面加const这个变量相当于常量)
2.函数定义,换用内联函数。 (编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率)