目录
什么是C++
C++关键字
命名空间
命名空间定义
普通的命名空间
命名空间可以嵌套
同一个工程允许存在多份命名空间
命名空间使用
1.加命名空间名称及域访问限定符(::)
2.使用using将命名空间导入
3.使用using namespace 命名空间引入
C++的输入和输出
缺省参数
缺省参数的分类
全缺省参数
半缺省参数
函数重载
函数重载概念
名字修饰
extern"C"
先来说说什么是C++.
C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计
c++关键字如下:
其中有很多c语言的关键字,新的关键字后面遇到会逐个讲解.
为什么要存在命名空间?
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员
1.先来看命名空间的定义
namespace liqi
{
//既可以定义变量,又可以定义函数
int x = 0;
int y = 0;
int Sub(int left, int right)
{
return left - right;
}
}
namespace hmy
{
int a;
int b;
int Add(int left, int right)
{
return left + right;
}
//hmy命名空间里嵌套了liqi命名空间
namespace liqi
{
int c;
int d;
int Sub(int left, int right)
{
return left - right;
}
}
}
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
namespace liqi
{
int x = 0;
int y = 0;
int Sub(int left, int right)
{
return left - right;
}
}
namespace liqi
{
int a = 0;
int Add(int left, int right)
{
return left + right;
}
}
这里我们写一段测试程序试一下.
int main()
{
std::cout << liqi::Sub(2, 1) << std::endl;
std::cout << liqi::Add(2, 1) << std::endl;
return 0;
}
如果可以运行则说明结论正确.
和我们预想的一样.
注意:一个命名空间相当于定义了一个新作用域,命名空间里所有内容都局限于该命名空间中.
既然我们定义了命名空间,那么我们改怎么使用它呢?
有如下三种办法:
刚才其实已经使用了一下,现在正式讲一下
既然每一块命名空间都是一个独自的作用域,那么如果多个命名空间里有多个相同的变量a怎么办?
那我们要直接访问a,编译器怎么知道我们想访问的是哪个a呢,所以须在变量名前加上:
命名空间和域访问限定符
代码如下:
#include
namespace liqi
{
int x = 4;
int y = 0;
int z = 6;
int Sub(int left, int right)
{
return left - right;
}
}
namespace hmy
{
int x = 6;
int y = 0;
int z = 9;
int Add(int left, int right)
{
return left + right;
}
}
int main()
{
std::cout << liqi::x << std::endl;
std::cout << liqi::y << std::endl;
std::cout << liqi::z<< std::endl;
std::cout << hmy::x << std::endl;
std::cout << hmy::y << std::endl;
std::cout << hmy::z << std::endl;
return 0;
}
我们运行一下
406,609。结果完全正确
我们发现,每次访问命名空间的成员,都要加限定符::,如果频繁的访问命名空间里的某个成员,这样写就会太繁琐,所以我们使用using这个关键字来解决.
格式为:
using 命名空间名称::成员变量
看以下代码来理解
#include
namespace liqi
{
int x = 4;
int y = 0;
int z = 6;
int Sub(int left, int right)
{
return left - right;
}
}
using liqi::x;
int main()
{
std::cout << x << std::endl;//这样我们使用liqi命名空间里的x便不需要再加::了,因为已经用using导入了.
return 0;
}
但是如果有重新定义的局部变量,则局部变量优先
代码如下来解释:
namespace liqi
{
int x = 4;
int y = 0;
int z = 6;
int Sub(int left, int right)
{
return left - right;
}
}
using liqi::x;
int main()
{
std::cout << x << std::endl;
int x = 0;
std::cout << x << std::endl;
return 0;
}
运行结果如下:
这就是using的使用了.
如果想把整个命名空间里的内容都导入,而不是单纯的几个变量,那么格式如下:
using namespace 命名空间名称.
C++自带一个标准命名空间 std,里面包含了各种常用的函数
例如cout,cin,endl这些等等.
可以发现我之前使用cout和endl的时候都加着std::
因为它是std命名空间里的
所以如果我们每次想直接使用这些函数,而不想加std::,则可以按刚才所说的
using namespace std;
这样就把std命名空间里的函数导入到这个工程中了.后面再用cin,cout这些函数时直接调用即可.
这也是为什么之前有见过C++的程序,为什么要在程序的前面加一个
using namespace std了.
使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空间。
使用C++输入输出更加方便一些.
不需要手动控制数据类型输入输出.
#include
using namespace std;
int main()
{
int a;
double b;
char c;
cin >> a;//直接输入
cin >> b >> c;//还可以连着输入两个数据
//需要注意的是。输入是大于的符号,输出是两个小于的符号,代表流,后面会将
//cout输出的时候,不加双引号的内容默认为变量,加双引号才被认定为字符串.
cout << a << endl;
cout << b << " " << c << endl;
return 0;
}
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参.
意思是呢,如果你没有给它传参数,他就会采用给取的默认值.
看下面例子先来简单认识一下:
void print(int a = 0)
{
cout << a << endl;
}
int main()
{
print();
print(10);
}
输出结果:
可以发现第一次调用print函数的时候,没有给参数,所以编译器就会采用给取的默认值0.
第二次调用给了一个参数10,所以编译器使用指定的实参10,并输出出来.
既函数里面每个参数都有给定默认值.
如下:
void print(int a = 0,int b = 1, int c = 2)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
需要注意的是半缺省参数并不是一般的参数是缺省的,而是一部分参数是缺省的.
1.半缺省参数必须是从右向左依次给出缺省值,不能有间隔.
2.缺省参数不能在函数声明和定义同时出现
3.缺省值必须是常量或全局变量
4.C语言不支持缺省参数
正确应用:
void print(int a, int b = 1, int c = 2);
错误示范:
void print(int a = 1, int b = 2, int c);//错误:缺省值必须向右到左依次给值,这个是从左到右
void print(int a = 1, int b, int c = 2)//错误,必须是从右向左 依次 给值,这个有间隔
//错误:缺省参数不能在函数声明和定义同时出现
void print(int a = 1, int b = 2, int c = 3);
void print(int a = 1, int b = 2, int c = 3)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
注意上面说的同名函数里面没有说返回值不同构成重载. 和返回值无关.
例如下面几个函数:
int Add(int x, int y)
{
return x + y;
}
int Add(short x, short y)
{
return x + y;
}
int Add(int x, int y, int z)
{
return x + y + z;
}
上面几个函数都构成重载,第一个和第二个参数类型不同,第一个和第三个参数数量不同.
这样就对重载有了一个大概的认识了.
那么编译器怎么知道要调用哪个函数呢?
编译器会根据传入的参数数量和类型自动选择最匹配的函数进行调用.
为什么C++会支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下阶段:
预处理、编译、汇编、链接
这里简单说一下各阶段所做的事情
编译:
预处理:头文件展开、宏替换,条件编译,去注释(生成.i文件)
编译:语法检查,生成汇编代码(生成.s文件)
汇编:形成符号表,把汇编代码转化为二进制机器码(生成.o文件)
链接:
1.合并段表 2.符号表的合并和符号表的重定位.
1. 实际我们的项目通常是由多个头文件和多个源文件构成,而通过我们C语言阶段学习的编译链接,我们可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
2. 所以链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起
3. 那么链接时,面对Add函数,连接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
4. 由于Windows下vs的修饰规则过于复杂,而Linux下gcc的修饰规则简单易懂,下面我们使用了gcc演示了这个修饰后的名字.
5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长+函数名+类型首字母】
先来看看采用c语言编辑器编译之后的结果:
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
而c++编译之后的结果如下:
在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息
添加到修改后的名字中。
这就是为什么C++可以函数重载,而C语言不可以了.
因为最后链接符号表找函数时,C语言的符号表里函数只要名字相同,无论参数多少,类型,都只是存放它的名字。所以无法识别参数的类型或数量
而C++链接时符号表里,不仅保存了函数的名字,还保存了参数的各种类型和数量,这就与其它重名函数但参数有差别的函数区分开了,所以C++支持重载.
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决