目录
- 简介
- 命名空间
- 为何会有命名空间
- 命名空间的定义
- 嵌套定义
- 命名空间的使用
- 作用域限定符
- using 将命名空间中某个成员引入
- using namespace 将该空间所有成员引入
- 缺省参数
- 全缺省参数函数
- 半缺省参数函数
- 如何给缺省值
- 函数重载
- 函数重载的概念
- 函数重载的三种类型
- 1:函数参数类型不同
- 2:参数个数不同
- 3:参数类型顺序不同
- 分析与总结
- linux下函数修饰规则
- Windows下修饰规则
- 总结
简介
经过许久的C语言学习,前不久终于开始了c++的学习经过总结写下了这篇文章.
总结:C++总计63个关键字,C语言32个关键字
废话少说直接开始第一个知识点
命名空间
首先按照学习一门语言的惯例写一段 Hello world 入门代码.
刚开始学能你不清楚为什么我只打印一句 Hello world 包含头文件我理解但为啥还要带一句 using namespace std 。有人可能表示这是命名空间书上说过,对大致上可以这么理解。
但是你知道他为啥要这么定义吗?定义规则是什么吗?自己该如何定义吗?
如果都知道,很高兴的和你说您可以跳过本篇这个知识点了
为何会有命名空间
一个组,一起开发一个大型项目。但是他们但他们编写程序时就可能出现重名的函数,变量,结构体等,合并时就出现问题了大量的命名冲突。最初,人们就想了个办法,将名字取得更长更复杂来避免重复,可这样做却给编写和阅读带来了困难。后来,人们创造了命名空间这个概念,它能有效地指出某个标示符到底属于哪个库。
命名空间的定义
定义命名空间,需要使用到namespace 关键字,后面跟命名空间的名字,然后接一对{ }即可,{ }中即为命名空间的成员。
如下 zgb 是命名空间的名字,大括号中的 sum 变量和 Add 函数及结构体 book 是 zgb 空间的成员。
namespace zgb
{
int sum = 10;
int Add(int x, int y)
{
return x + y;
}
struct book
{
char name[15];
}mm;
}
嵌套定义
可如下嵌套定义命名空间
namespace binbang
{
int a = 29;
namespace binbin
{
int Add(int x, int y)
{
return x + y;
}
}
}
命名空间相同时如相互的成员名均不同则会合并相同的命名空间,但成员名相同时则会发生命名冲突从而报错。
总结:
1:命名空间中可以定义变量,函数,结构体等非常自由。
2:命名空间可以嵌套定义
3:注意若同一工程中,命名空间名字如果相同,相同空间名中的成员会合并为一个命名空间,但这就丧失了命名空间的作用,因为由于是合并而成的里面的命名可能冲突导致报错
4:只要命名空间名字不同,每个命名空间是互不干扰的就像局部变量一样
命名空间的使用
命名空间的使用有三种方式:
作用域限定符
使用方法:命名空间名 : : 该空间成员(变量函数结构体等),符号左边写命名空间名,右边写该该空间成员
举例如下:
嵌套时也是如图加作用域限定符即可
学会了使用现在咱验证一下不同命名空间中同名变量是否会冲突,答案是不会。如图
using 将命名空间中某个成员引入
使用方法:using 命名空间名 : : 该空间成员
如下图, using bb::sum;等于是声明了 bb 中的变量 sum,后面可以不加作用域限定符即可直接使用。注意如果该语句在全局中则该声明全局有效,如该语句在局部则该声明局部有效。
using namespace 将该空间所有成员引入
使用方法:using namespace 命名空间名称
作用:将该空间所有成员引入,如下面using namespace std,就是将命名空间 std 中的内容全部展开(声明/引入),其中就包括了cout 。所以可以之后cout可以直接使用;
注意:该命令如在全局中使用,那将使命名空间失去意义,如果全局中与 引入(声明/展开)成员同名的变量或函数等将发生冲突,不同命名空间均在全局或同一个局部空间使用using namespace 将该空间所有成员引入(声明/展开)时也可能发生冲突。
using namespace,一般在刷题,测试及局部中使用比较合适,工程和全局中可控性太低
缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
如下图,test7 函数中有一个缺省值 x = 10,当调用 test7 函数时传参为22则,x 等于22。当调用 test7 函数时参数为空则 x 默认等于缺省值10;
全缺省参数函数
概念:指函数的参数均为均有缺省值
如下
void test9(int x = 10, int y = 20, int z = 30)
{
cout << x << endl;
cout << y << endl;
cout << z << endl;
}
半缺省参数函数
概念:指函数的部分参数有缺省值,注意参数必须从右往左连续缺省,简要记无缺省值参数不能在缺省值参数的右边。
如下是半缺省参数函数
void test9(int x , int y = 20, int z = 30)
{
cout << x << endl;
cout << y << endl;
cout << z << endl;
}
为啥说无缺省值参数不能在缺省值参数的右边呢?
如下无缺省值参数出现在缺省值参数的右边,程序直接报错了。因为函数传参时是从左到右一 一对应的不能跳过中间参数进行传参,所以没有缺省值的参数需要在右边并且连续,达到优先及易区分等目的。
如何给缺省值
在实际使用中.c文件中的函数须在.h中声明时,如果要声明的函数带缺省值,应在.h文件中给缺省值,定义函数时不用给缺省值,如果两个都给缺省值那编译器将会报错,因为不知道该用那个
如下编译器将会报错
//a.h (.h文件)
void zgb(int a = 10);
// a.cpp (.cpp文件)
void zgb(int a = 20)
{}
因为声明都没有缺省值那,函数的缺省值又从何而来呢
正确方式
//a.h (.h文件)
void zgb(int a = 10);
// a.cpp (.cpp文件)
void zgb(int a )
{}
总结:
- 半缺省参数必须从右往左依次来给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现
- 缺省值必须是常量或者全局变量
- C语言不支持(编译器不支持)
函数重载
函数重载的概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
函数重载的三种类型
1:函数参数类型不同
如下函数同名,但函数参数类型不同,如下当函数调用时传参为int 类型则会调用函数参数为int类型的Add函数,反之则调用参数为double类型的Add函数;
//函数重载
int Add(int x, int y)
{
cout << "Add(int x, int y)" << endl;
return x + y;
}
double Add(double x, double y)
{
cout << "Add(double x, double y)" << endl;
return x + y;
}
void tese2()//函数重载,函数参数类型不同
{
Add(1, 2);//编译时就链接好了对应函数
Add(1.3, 1.5);
}
2:参数个数不同
如下函数同名,但函数参数个数不同,如下当函数调用时只传一个参数则使用一个参数Add函数,当函数调用时传两个参数则使用两个参数Add函数。(当然传参与函数参数需相同)
int Add( int y, char x)
{
cout << "Add( int y, char x)" << endl;
return y;
}
int Add(int x)
{
cout << "Add(int x)" << endl;
return x;
}
void tese4()//函数重载,函数参数个数不同
{
Add(1);
Add(2, 4);
}
3:参数类型顺序不同
如下函数同名,但函数各参数类型顺序不同,如下当调用函数传参时参数1为 char 类型参数二为 int 类型,则会调用 int Add(char x, int y)
当调用函数传参时参数1为 int 类型参数二为 char 类型,则会调用 int Add( int y, char x)
int Add(char x, int y)
{
cout << "Add(char x, int y)" << endl;
return x;
}
int Add( int y, char x)
{
cout << "Add( int y, char x)" << endl;
return y;
}
void tese3()//函数重载,函数参数顺序不同
{
Add('a', 1);
Add(1, 'a');
}
分析与总结
1:为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
- 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。
- 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
- 链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
- 由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,下面我会使用g++演示函数修饰后的名字。
- 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。
linux下函数修饰规则
首先看看采用C语言编译器编译后结果,如下图在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
在看看采用c++编译器编译后的结果,如下图在linux下,采用g++编译完成后,除main函数外的函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
Windows下修饰规则
由于Windows下Vs编译时函数的修饰规则较为复杂还是直接看下图吧
对比Linux会发现,windows下vs编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了。
总结
- 通过上述举例应该理解了C语言没办法支持重载,是因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
- 注意:如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。
- 重载函数在运行时被调用怎么找到对应函数?在程序链接时,每条函数调用指令就已经链接好了对应函数地址,所以运行时使用重载函数效率也不会降低。
C++入门第一期内容就这些了,下期会更新引用/内联函数/auot等