【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

=========================================================================

相关代码gitee自取:

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期:

【数据结构初阶】十一、归并排序(比较排序)的讲解和实现

(递归版本 + 非递归版本 — C语言实现)-CSDN博客

 =========================================================================

                     

引言:什么是C++

  • C语言是结构化和模块化的语言,适合处理较小规模的程序。
    对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言就不合适了。

    为了解决软件危机,20世纪80年代,计算机界提出了:
    OOP(object oriented programming:面向对象)思想,
    支持面向对象的程序设计语言应运而生

                             

  • 1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,
    发明了一种新的程序语言,为了表达该语言与C语言的渊源关系,命名为C++

                             

  • 因此:C++是基于C语言而产生的,

    它即可以进行C语言的过程化程序设计,
    又可以进行以抽象数据类型为特点的基于对象的程序设计,
    还可以进行面向对象的程序设计

                      

  • C++是在C语言的基础之上,容纳了面向对象编程思想,

    并增加了许多有用的库,以及编程范式等。

    熟悉C语言的话,对C++的学习有一定的帮助

                    

  • 本篇博客主要目标:

    1、补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,

    如:作用域方面、IO方面、函数方面、指针方面、宏方面等

    2、为后续了解类和对象打下基础

           

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

引言:C++的发展史

1979年,贝尔实验室的本贾尼等人试图分析unix内核的时候,
试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,
完成了一个可以运行的预处理程序,称之为 C with classes

                    

C++语言也进行着逐步递进、由浅入深的过程

C++的历史版本:
阶段 内容
C with classes

类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符

重载等

C++1.0 添加虚函数概念,函数和运算符重载,引用、常量等
C++2.0 更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静态成员以及const成员函数
C++3.0 进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处理
C++98 C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)
C++03 C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多异性
C++05

C++标准委员会发布了一份计数报告(Technical Report — TR1),正式更名为C++0x,

即:计划在本世纪第一个10年的某个时间发布

C++11 增加了许多特性,使得C++更像一种新语言,

如:正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等

C++14对C++11的扩展,主要是修复C++11中的漏洞以及改进,

如:泛型的lambda表达式,auto的返回值类型推导,二进制字面常量等

C++17在C++11上做了一些小幅改进,增加了19个新特性,

如:static_assert()的文本信息可选,Fold表达式用于可变的模板,

if 和 switch 语句中的初始化器等

C++20自C++11以来最大的发行版,引入了许多新的特性,

如:模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)

等重大特性;

还有对已有特性的更新,如:Lambda支持模板、范围for循环支持初始化等

C++23制定中……

            

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

              

一、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 — 命名空间关键字

(1). 命名空间的作用:

在C/C++中,变量、函数和后面要了解的类都是大量存在的,

这些变量、函数和类的名称都将存在于全局作用域中,可能会导致很多冲突。

使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,
namespace关键字的出现就是针对这种问题的

示例:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                     

                     


                    

(2). 命名空间的定义:

  • 定义命名空间,需要使用到namespace关键字,

    关键字后接命名空间的名称,然后再加上一对大括号{}即可,

    大括号{}中内容为命名空间的成员

                      

  • 在一般开发中,会使用项目的名称作为命名空间的名称

                            

一般命名空间的定义:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

命名空间嵌套子命名空间:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

存在多个相同名称的命名空间:
  • 同一个工程中允许存在多个相同名称的命名空间,
    编译器最后会合并成同一个命名空间

                 

  • 一个工程中的 test.h(头文件) 和 text.cpp(C++文件)中
    两个同名命名空间会被合并成一个

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                     

                     


                    

(3). 命名空间的使用:

定义一个命名空间就定义了一个新的作用域,

命名空间中的所有内容都局限于该命名空间中,

所以不能通过直接调用来使用命名空间中的内容,

命名空间的使用有三种方式:

                      

方式一:

加命名空间名称和作用域限定符

  • 作用域限定符(两个冒号) —   : :

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                       

方式二:

using namespace 命名空间名称 展开命名空间

  • 使用 using namespace 命名空间名称 可以展开对应的命名空间,
    展开后可以直接通过该命名空间中成员的名称使用该成员

                        

  • 但是使用该方式对命名空间的展开,
    会导致命名空间的所有内容暴露出来,
    可能又会导致命名冲突问题

                   

  • 所以一般在自己使用时为了方便才会使用该方式,

    如果是项目工程,该方式一定要慎重使用,可能会出大问题的

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                 

方式三:

使用using关键字只展开命名空间中的某个成员

  • 通过方式二直接展开命名空间会有命名冲突的风险,

    那么我们可以通过:
    using 命名空间名称::指定成员

    来指定只展开命名空间中的某个成员

                         

  • 这种方式是比较常用的,

    通常是对一些常用的成员(对象)进行使用,

    来避免频繁使用方式一调用命名空间,减轻代码冗余

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

            

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

三、C++输出&输入

C++中有了新的输入和输出方法,虽然说有了新的输入输出方法,

但之前C语言中的输入和输出方法也是可以用的,

在了解C++的输入和输出方法前,需要先了解以下概念:

                     

                     

std — C++标准库命名空间

std是C++标准库的命名空间名,C++将标准库的定义实现都放在这个命名空间中,
上一标题我们知道了使用命名空间的三种方式,而使用std命名空间的方式要考虑实际情况:

               

std命名空间的使用惯例:

  • 在自己的日常练习中,建议直接方式二即可,

    即:using namespace std

    这样使用std命名空间就很方便了,不用频繁使用方式一进行操作

                     

  • 而在项目工程中,using namespace std 展开(方式二展开),
    标准库就全部暴露出来了,如果我们定义了跟库中重名的 类型/对象/函数 ,

    就会存在命名冲突问题。该问题在日常练习中很少出现,

    但是项目开发中代码较多、规模大,就很容易出现。

                        

  • 所以建议在项目开发中使用方式三指定展开,

    即:using std::成员(对象)名

    来指定展开std命名空间中常用的几个库对象/类型/成员,

    像是C++输出时使用的cout,输入时使用的cin

                         

                         


                        

cout 和 cin

  • cout  —  console(控制台) out  —  标准输出对象(控制台)  —  流插入
    cin  —  console(控制台) in  —  标准输入对象(键盘)  —  流提取

                        

  • 使用cout标准输出对象和cin标准输入对象时,
    需要包含 头文件(IO流头文件)以及 按命名空间使用方法使用std
注:C++ 头文件 不需要像C语言一样加“.h”后缀

早期标准库将所有功能在全局域中实现,声明在“.h”后缀的头文件中,
使用时只需包含对应头文件即可,后来将其(标准库所有功能)实现在std命名空间下,
为了和C语言头文件区分,也为了正确使用命名空间,规定C++头文件不加“.h”后缀。

旧编译器(vc 6.0)中还支持 格式,后续编译器已不支持,

因此推荐使用 + std命名空间 的方式

                   

  • cout 和 cin 是全局的流对象,endl(endline)是特殊的C++符号,表示换行输出,
    他们都包含在 头文件 中

                         

  • 使用cout进行输出时还需要用到: <<  —  流插入运算符

    使用cin进行输入时还需要用到: >>  —  流提取运算符

    (在C语言中,<> 是位于算符,在C++中又多了以上身份)

                    

  • 使用C++输入和输出相对C语言更方便,
    不需要像 scanf / printf 输入输出时要手动控制格式(%d、%p……),
    C++的输入和输出可以自动识别变量类型

                         

  • 实际上 cout 和 cin 分别是 ostream 和 istream 类型的对象,
    >> 和 << 也涉及运算符重载等知识,这里只是简单了解并使用
    关于cout和cin还有很多更复杂的用法,

    比如控制浮点数输出精度,控制整型输出进制格式等,但并不常用,
    实在需要使用时可以用C语言来操作(C++兼容C语言的操作)

示例:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

对应代码:
//包含IO流头文件:
#include 

//指定展开命名空间成员:
using std::cout;  //指定展开标准输出对象(控制台)
using std::cin;  //指定展开标准输入对象(键盘)
using std::endl;  //指定展开C++换行符号

int main()
{
	int a = 10; //整型变量
	double b = 3.14; //浮点型变量

	cout << "使用cout打印当前a和b:" << endl;

	//使用cout进行输出:
	cout << a << endl << b << endl;
	/*
	* 通过cout标准输出对象和<<流插入运算符进行输出打印:
	* 
	* 先将a这个变量流进std::cout这个控制台中打印,
	* 再进行endl换行,再将b这个变量
	* 流进std::cout这个控制台中打印,再换行。
	* 
	* 即使 a变量 和 b变量 的类型不同也能打印
	* C++的输入和输出可以自动识别变量类型
	*/
	
	cout << "使用cin分别输入数据到a和b:" <> a >> b;
	/*
	* 通过cin标准输入对象和>>流提取运算符对数据进行输入:
	*
	* 让你在控制台上输入的数据分别流入a和b这两个变量中
	*
	* 即使 a变量 和 b变量 的类型不同也能输入
	* C++的输入和输出可以自动识别变量类型
	*/

	cout << "输入后再使用cout进行输出打印:" << endl;

	cout << a << endl << b << endl;

	return 0;
}

            

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

四、缺省参数

(1). 缺省函数的概念:

缺省参数是在声明或定义函数时为函数的参数指定一个缺省值。

在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,

有了缺省参数后,可以调整参数的各种形式来调用该函数

                        

  • 缺省值必须是常量或者全局变量

                    

  • C语言不支持缺省参数(编译器不支持)

                             

  • 缺省参数不能在函数声明和定义中同时出现

                     


                    

(2). 缺省函数的分类:

全缺省参数:

函数的所有参数都设置对应的缺省参数

图示:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                          

                   

半缺省参数:

只对函数的部分参数设置对应的缺省参数

                       

  • 半缺省参数必须从右往左依次来给出,不能间隔着给
    (传参“从左往右”传,半缺省参数“从右往左”给)
图示:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

            

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

五、函数重载

自然语言中,一个词可以有多重含义,

人们可以通过上下文来判断该词真实的含义,即该词被重载了

                           

函数重载的概念:

函数重载是函数的一种特殊情况,
C++允许在同一作用域中声明几个功能类似的同名函数,
这些同名函数的形参列表(参数个数、参数类型、类型顺序)不同,
函数重载常被用来处理实现功能类似但数据类型不同的问题

图示:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                     

                     


                    

(难)C++支持函数重载的原理 — 名字修饰(name Mangling)

为什么C语言不支持函数重载,而C++支持函数重载?

要解决这个问题,需要先了解一下编译链接的过程,往期相关博客:
学C的第三十四天【程序环境和预处理】_高高的胖子的博客-CSDN博客

                        

简单解释编译链接过程:

假设我们现在有三个文件:

Func.h                Func.cpp                Test,cpp

(函数声明)        (函数实现)        (主函数调用)

执行主函数时需要进行以下过程:

预处理  =>  编译  =>  汇编  =>  链接

现有文件和其内容:

【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                     

预处理:
  • 预处理过程操作包括:头文件展开(主要)  /  ​​​宏替换  /  条件编译  /​  去除注释​​​

在 Fun.cpp文件 和 Test.cpp文件 中,因为都包含了 Func.h头文件 ,

所以在预处理时会对头文件进行展开,之后会生成预处理文件:Func.i文件 和 Test.i文件,

所以在 Func.i文件 中就会有Func函数的声明和实现(Func.i:函数声明和定义),

而 Test.i文件 中会有被调用的函数的声明和调用(Test.i:函数的声明和实际调用)

                           

编译:
  • 编译过程操作包括:检查语法是否错误 / 生成汇编代码

进行编译时会生成汇编代码文件(.s文件),

即 Func.s文件 和 Test.s文件(分别由 Func.i文件 和 Test.i文件 生成),

Func.s文件 中存放了两个重载函数对应的汇编代码,

而 Test.s文件 中则存放了主函数(main函数)的汇编代码,

包括被调用的两个重载函数的汇编代码,

而要调用这两个重载函数,还需要用到汇编语言中的 call指令 来获取函数的地址。

但在编译阶段,因为 Test.i文件 中只包含了 Func.h头文件,只有函数声明,没有函数实现,

所以 call指令 还无法获得对应的函数地址。

在这种情况下,编译器会判断调用的函数和头文件中函数是否匹配,如果匹配的话,

即使 call指令 还没找到函数地址,也可以先让其通过编译(方便实现多文件项目)

  • 【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

                  

汇编:
  • 汇编过程操作包括:将汇编代码文件中的代码转换为二进制的机器码

    (二级制的机器码:CPU能“读懂”的代码)

汇编后会生成目标文件(.o文件),

即 Func.o文件 和 Test,o文件(分别由 Func.s文件 和 Test.s文件 生成),

两个文件都将汇编代码转换成了对应的二进制机器码

                 

链接: 
  • 链接过程操作包括:将目标文件链接合并到一起,链接一些没有确定函数地址等等

将汇编操作中的 Func.o文件 和 Test.o文件 合并为 a.out文件(默认情况下),

合并后的 a,out文件 中:

之前在编译过程中call指令未找到的函数地址,可以在合并后的 a.out文件 中找到

(因为合并前的 Func.o文件 就包含对应函数的实现)

                          

                          

———————————————————————————————

                       

C++支持重载函数,而C语言不支持的原因:

通过上面对编译链接过程的简单了解,我们可以知道:

在编译过程中 call指令 还未找到被调用函数的地址,直到链接过程合并文件后才能够找到

                   

C语言不支持重载函数的原因:

在C语言中,没有重载函数,即函数名唯一的情况下,

要找函数地址只需要通过唯一的函数名即可找到,

即在链接过程中通过唯一的函数名在 Func.o目标文件 中的符号表进行对地址的查找,

所以如果C语言中有重载函数,函数名不唯一的情况下就无法在链接过程中找到函数地址,

因此C语言无法支持重载函数

                      

C++支持重载函数的原因 — 名字修饰(name Mangling):

(注:不同编译器实现方式不同,这里以Linux中的g++为例)

在C++中有重载函数的情况下,即函数名不唯一的情况下,

C++有一个函数,可以通过函数名和参数情况修饰出一个新的函数名字,

函数名相同但参数情况不同,就能修饰出不同的函数名字,

再通过修饰出的函数名字来查找对应的函数地址。

  • 修饰名字构成方式:
    _Z  +  函数名字符个数  +  函数名  +  各参数首字母

    假设有一个函数:Func(int a, double b) ,修饰后的函数名字为:_Z4Funcid

    假设有另一个函数:Func(double b, int a) ,修饰后的函数名字为:_Z4Funcdi

    所以即使函数名相同,也可以通过参数情况来创建出不同的函数名字

  • 【C++初阶】一、入门知识讲解(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://net2asp.com/8e40a32e3d.html