文章目录
- 前言
- 一、C++关键字(C++98)
- 二、命名空间
- 命名空间介绍
- 命名空间的使用
- 三、C++输入【cin】& 输出【cout】
- 四、缺省参数
- 缺省参数概念
- 缺省参数分类
- 缺省参数的使用
- 小结一下
- 五、函数重载
- 函数重载介绍
- 函数重载类型
- 六、C++支持函数重载的原理--名字修饰(name Mangling)【重点】
前言
从今天开始就序列更新C++的文章了,希望大家可以耐心的坚持下去学习,有一个很好的成长~~
首先来看一下C++的介绍,百度百科C++ – >点我跳转
C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式
等。熟悉C语言之后,对C++学习有一定的帮助,本章节主要目标:
- 补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,比如:作用
域方面、IO方面、函数方面、指针方面、宏方面等。- 为后续类和对象学习打基础。
- 我们这里不讲和C语言重合的语法,直接上干货,新内容,可以关注一下C语言的专栏
- 下面直接开始C++之旅~~
C语言有关键字,那么C++也有关键字,首先我们来认识一下这些关键字
一、C++关键字(C++98)
- C++总计63个关键字,C语言32个关键字
- 这些关键字我们已经在C语言阶段已经有些见到过了,那些没有见过的关键字我们在后续的练习中会反复用到,所以也不用刻意记住慢慢的就都知道了~~
asm | do | if | return | try | continue |
---|---|---|---|---|---|
auto | double | inline | short | typedef | for |
bool | dynamic_cast | int | signed | typeid | public |
break | else | long | sizeof | typename | throw |
case | enum | mutable | static | union | wchar_t |
catch | explicit | namespace | static_cast | unsigned | default |
char | export | new | struct | using | friend |
class | extern | operator | switch | virtual | register |
const | false | private | template | void | true |
const_cast | float | protected | this | volatile | while |
delete | goto | reinterpret_cast |
第二个我们来认识一个新的概念叫命名空间
二、命名空间
命名空间介绍
-
C++ 的命名空间(Namespace)是一种用于组织和管理代码标识符(变量、函数、类等)的方法。它可以避免命名冲突,使得在不同的命名空间中可以使用相同的标识符而不会产生命名冲突。
-
使用命名空间可以将一组相关的标识符封装在一起,提供了更好的代码组织和模块化能力。在 C++ 中,可以通过关键字 namespace 定义一个命名空间,并在其中声明相关的标识符。
- 我们来看下面代码,定义了一个全局变量rand,而我还引入了头文件stdlib,而这个rand是stdlib中的一个库函数,–>rand,这样我想打印这个rand的值就会与库函数里的rand产生冲突,产生重定义,这个就是C语言中的不足之处了
#include <stdio.h>
#include <stdlib.h>
int rand = 10;int main()
{printf("%d\n", rand);return 0;
}
- 在C++中就可以很好的解决这种问题:
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
- 首先第一个正常的命名空间定义,也就是namespace后面跟上一个名字【随便起~】,也就相当于是一个域,就好像C语言中的局部域,全局域。
namespace lsl
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}
- 第二个就是命名空间可以嵌套
- 这个也可以很好的理解,就和函数嵌套一样,一个域是一个域的,互不干扰
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}}
}
- 第三个就是同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
-
那么我这个命名空间域如何使用呢?
-
来看下面这段代码
namespace lsl
{// 命名空间中可以定义变量/函数/类型int a = 0;int b = 1;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}
int main()
{printf("%d\n", a);return 0;
}
- 上面图片清晰的展示到,是不能这样用的,已经是在一个域里面了,所以就要使用特定的方法来使用这个域里面变量,函数和类型
命名空间的使用
命名空间的使用有三种方式:
- 第一种方式加命名空间名称及作用域限定符【::】也就是两个冒号,也就是域作用限定符
int main()
{printf("%d\n", lsl::a);return 0;
}
- 第二种方式使用using将命名空间中某个成员引入
- 第三种方式就是使用using namespace 命名空间名称引入,也就是展开,让指定的命名空间里的内容在外面都可以访问
这里的最后一种方式相信大家学c++或多或少也都见到过,平常在使用c++的时候都要有这么一句using namespace std;,std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中,这里的展开和头文件的展开是不一样的,不要混淆了~~
到这里我们也知道了,首先是先从局部域来搜索,然后从全局域再搜索,搜索不到就报错了,然而我们这里新增加了一个域作用限定符【::】,有了这个就是指定域搜索
- 编译器搜索原则:
不指定域:当前局部域,全局域
指定域:如果指定了,直接去指定域搜索
三、C++输入【cin】& 输出【cout】
- 在学习一门语言的时候,大部分人都会第一次写下这么一段代码,打印
hello world
,C++怎么来打印呢?
#include<iostream>
using namespace std;
int main()
{cout << "hello world" << endl;return 0;
}
说明:
- 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含 < iostream >头文件以及按命名空间使用方法使用std
- cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含头文件中。后面在实际应用中会回用到很多,慢慢都会介绍~~
<<
是流插入运算符,>>
是流提取运算符。- 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。
-
注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用+std的方式。
-
这里的cout【输出】,cin【输入】可以自动识别类型
std命名空间的使用惯例:
std是C++标准库的命名空间,如何展开std使用更合理呢?
- 在日常练习中,建议直接using namespace std即可,这样就很方便。
- using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 +using std::cout展开常用的库对象/类型等方式。
四、缺省参数
缺省参数概念
- 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。
- 在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
接下来看一个案例就能明白了:
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func(); // 没有传参时,使用参数的默认值Func(10); // 传参时,使用指定的实参return 0;
}
缺省参数分类
- 全缺省参数【也就是从左到右形参都有默认值】
void Func(int a = 10, int b = 20, int c = 30)
{cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
}
- 半缺省参数【必须是从右到左赋值,不可以从左到右】
void Func(int a, int b = 10, int c = 20)
{cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
}
缺省参数的使用
- 我们可以这样使用:
- 第一个参数就是匹配的一个形参,依次匹配,不可跳跃的传参
- 实际上我们是还可以这样用的,比如说我是定义了一个栈,栈在初始化的的时候不知道一开始初始化多少个,这个时候我们就可以使用缺省参数,指定传参。
- 比如我知道要插入100个数据,我就直接指定100的大小就可以,减少扩容消耗
- 如果不知道多少个就默认4个就可以啦~~
还有一个重要的点,就是下面这里
- 我们这里可以分文件写这个栈
- 我们这里的声明和定义可以同时写吗?
我们有人写代码的时候就有可能写成这样的操作,声明的参数和定义的参数不一样,那么是依谁的为准呢?
- 我们这里直接给出结论,是必须在声明给,为什么呢?
- 这就需要了解代码的编译原理了,到时候会发表一篇C语言的编译程序环境和预处理,程序编译+链接全过程,尽情期待~~
- 在同一文件下,缺省值在函数的声明与定义中同时定义,在编译阶段的语法检查时,直接报错了~
小结一下
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现
3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)
五、函数重载
函数重载介绍
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
函数重载类型
函数重载有3个类型,我们分别来看一下:
- 第一个就是参数类型不同
第二个是参数个数不同
- 第三个就是参数类型顺序不同
- 最后这里要注意:构成函数重载的条件是函数名相同,参数不同,构成函数重载【非常重要】
下面我们来看一道题【自己先做一下】来巩固一下:
以下不是double compare(int,int)的重载函数的是( )
A. int compare(double,double)
B. double compare(double,double)
C. double compare(double,int)
D. int compare(int,int)
解析:
A.重载必须是参数列表有所不同(包括个数和类型),所以参数类型不同,构成重载
B.参数类型不同,构成重载
C.参数类型不同,构成重载
D.函数重载不能依靠返回值的不同来构成重载,因为调用时无法根据参数列表确定调用哪个重载函 数,故错误
六、C++支持函数重载的原理–名字修饰(name Mangling)【重点】
那么为什么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+函数长度+函数名+类型首字母】。
- gcc编译后的结果:
结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变,之所以就函数名不能相同
- 采用C++编译器编译后结果:
结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参
数类型信息添加到修改后的名字中,所以就支持函数重载
- Windows下名字修饰规则
-
对比Linux会发现,windows下vs编译器对函数名字修饰规则相对复杂难懂,但道理都是类似的,我们就不做细致的研究了。
-
【扩展学习:C/C++函数调用约定和名字修饰规则–有兴趣好奇的同学可以自行搜索看一看,里面有对vs下函数名修饰规则讲解】
-
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
-
如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办
法区分。
最后,本章简单的介绍了C++的关键字,介绍了命名空间的作用和使用,知道了C++的输出和出入,会用缺省参数,以及函数重载~~