【C++】入门(一):命名空间、缺省参数、函数重载

目录

一、关键字

二、命名空间

问题引入(问题代码):

域的问题

1.::域作用限定符 的 用法:

2.域的分类

3.编译器的搜索原则

命名空间的定义

命名空间的使用

举个🌰栗子:

1.作用域限定符指定命名空间名称

2. using 引入命名空间中的成员 即 展开命名空间中某一个

3. usinng namespace 命名空间名称 展开命名空间

三、C++输入、输出

四、缺省参数

概念

全缺省参数

半缺省参数

实践中的应用场景🌰举个例子:

声明和定义分离

回顾 声明 和定义的概念

再来分析上述程序

理解编译与链接的过程

理解函数与文件的关系

五、函数重载

代码示例:

C++是如何支持函数重载的?

函数名修饰


一、关键字

asmdoifreturntrycontinue
autodoubleinlineshorttypedeffor
booldynamic_castintsignedtypeidpublic
breakelselongsizeoftypenamethrow
caseenummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigneddefault
charexternoperatorswitchvirtualregister
constfalseprivatetemplatevoidtrue
const_castfloatprotectedthisvolatilewhile
deletegotoreinterpret_cast

增加的关键字: C++增加了一些关键字来支持面向对象编程(如类、继承、多态等)和模板编程。例如,class,public,protected,private,virtual,friend,template,typename等。这些关键字没有在C语言中。

类型增强:C++增加了一些用于类型安全和方便的关键字,如bool,true,false,using,namespace等。

异常处理:为了支持异常处理,C++引入了try,catch,throw等关键字。

新的转换操作符:C++提供了static_cast,dynamic_cast,const_cast和reinterpret_cast等关键字进行类型转换,这是C语言中所没有的。

增强的存储类说明符:C++引入了mutable和thread_local等存储类说明符。

模板编程:为了支持泛型编程,C++增加了template和typename关键字。

新增运算符:C++还定义了如new,delete等用于动态内存管理的关键字,这些在C中通常通过库函数如malloc和free来实现。

特殊成员函数关键字:C++还有如default和delete等关键字,用于特殊成员函数的声明,这样设计是为了提供更好的控制。

二、命名空间

问题引入(问题代码):

下面代码存在命名冲突 : rand变量 和头文件<stdlib.h>中声明的函数 rand() 名字相同 导致冲突。

#include<stdio.h>
#include<stdlib.h>   /*rand*/
int rand = 0;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespaceguan来解决
int main()
{printf("%d\n",rand);return 0;
}

域的问题

1.::域作用限定符 的 用法:

限定符左边是哪一个域名 就限定了访问该变量的范围

左边是空 默认是全局域

2.域的分类

  • 全局域

  • 局部域:如果不用限定符,默认访问局部域 局部优先

  • 命名空间域:为了防止命名冲突 eg.全局定义两个同名变量 ,防止重定义,C++提出就用关键字namespace把他们定义在不同命名空间域中。

  • 类域

    注意:

    全局域、局部域既会影响生命周期,也会影响访问。命名空间只影响访问

3.编译器的搜索原则 

 1️⃣当前局部域 2️⃣全局域 3️⃣如果指定了,直接去指定域搜索

命名空间的定义

正常定义

 // 正常的命名空间定义
namespace hhh
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};}

嵌套定义

举个栗子🌰:

namespace aaa
{namespace bbb{void Push(){cout<<"zs"<<endl;}}namespace ccc{void Push(){cout<<"yyy"<<endl;}}
}
int main()
{//嵌套定义在命名空间的同名函数 各自调用bit::bbb::Push();bit::ccc::Push();return 0;
}

ps:命名空间可以重名,编译器会把他们合并,只要命名空间内部不冲突就可以

命名空间的使用

命名空间到底该如何使用?

举个🌰栗子:

namespace yyy
{//命名空间中定义 变量 / 函数 /类型int a = 0;int b= 1;int Add(int left,int right){return left+right;}struct Node{struct Node* next;int val;};
}

1.作用域限定符指定命名空间名称

//指定访问
int main()
{//::作用域限定符printf("%d\n",yyy::a);return 0;
}

2. using 引入命名空间中的成员 即 展开命名空间中某一个

//展开一个
using yyy::b;
int main()
{printf("%d\n",yyy::a);//不可以 因为此时只展开了一个成员变量printf("%d\n",b);
}

3. usinng namespace 命名空间名称 展开命名空间

展开命名空间 影响的是 域的搜索规则。不展开命名空间,默认情况编译器只会在局部域、全局域搜索。展开命名空间就可以在命名空间里搜索。

//展开全部
using namespace yyy;
int main()
{printf("%d\n",yyy::a);//指定去该命名空间找变量aprintf("%d\n",b)
}

注意:

1. 日常练习展开为了方便使用可以展开std,实际工程实践中慎重使用!

2.展开命名空间 不是 等同于引入全局变量!

3.展开命名空间 跟 包含头文件 也有本质区别,包含头文件 在预处理过程中本质是拷贝头文件的内容

三、C++输入、输出

解释Hello world代码

//包含标准输入输出流库
#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;int main(){//cout和cin是全局的流对象,细说分别是ostream和istream类型的对象// <<是流插入运算符,>>是流提取运算符//endl是C++符号,表示endline换行//他们都包含在包含<iostream>头文件中cout<<"Hello world!!!"<<endl;return 0;}

说明:使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件 以及按命名空间使用方法使用std。

补充:std命名空间的使用习惯

1.日常练习:直接展开 using namespace std

2.项目开发:std::cout 使用时指定命名空间 + using std::cout 展开常用库对象

C++ 输入输出 自动识别变量类型 

  • 示例代码:
    #include <iostream>
    using namespace std;
    int main()
    {int a;double b;char c;// 可以自动识别变量的类型cin>>a;cin>>b>>c;cout<<a<<endl;cout<<b<<"  "<<c<<endl;return 0;
    }
    • 说明

cin>>a;这行代码从标准输入流(键盘)中接受一个整数,并将其存储在变量a中。cin会根据提供的变量类型自动解释输入数据。cin>>b>>c;这行代码首先从标准输入流中接收一个双精度浮点数,并将其存储在变量b中,然后接收一个字符并存储在c中。 

四、缺省参数

  • 概念

    声明或定义函数时为函数的参数指定缺省值。缺省值就是给形参设置一个默认值。调用函数时,如果没有指定实参,则使用参数的默认值。

    缺省值必须是 常量或者全局变量。一般使用常量。

    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;
    }
    调用Func()时,可以这样给参数int main()
    {Func(1,2,3);Func(1,2);Func(1);Func();//注意:不可以跳越传值//Func(,1,2);return 0;
    }
  • 半缺省参数

    注意:只能从右往左连续给缺省值,这样调用保证传的实参顺序不存在歧义

    void Func(int a, int b = 20, int c = 30)
    {cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
    }
    //调用 同样不能跳越给
    int main()
    {Func(1,2,3);Func(1,2);Func(1);
    }

实践中的应用场景🌰举个例子:

struct Stack
{int* a;int size;int capacity;//...
};
//StackInit()改造为半缺省函数 使得可以适用更多的需要开辟空间的场景
void StackInit(struct Stack* ps,int n=4)
{ps->a=(int*)malloc(sizeof(int)*n);
}
int main()
{struct Stack st1;//缺省参数 使得函数可以适应不同场景 // 1、确定要插入100个数据StackInit(&st1, 100);  // call StackInit(?)
​// 2、只插入10个数据struct Stack st2;StackInit(&st2, 10);   // call StackInit(?)
​// 3、不知道要插入多少个 //这时就可以使用函数定义里提供的 参数缺省值 //不知道插入多少个 可以先初始化四个空间struct Stack st3;StackInit(&st3);
​return 0;
}
  • 声明和定义分离

    回顾 声明 和定义的概念

    • 函数声明告诉编译器函数的名称、返回类型以及参数列表(类型、顺序和数量),但不涉及函数的具体实现。函数声明经常出现在头文件(.h)中

    • 函数定义:提供了函数的实际实现,它包括函数的主体,即函数被调用时将执行的具体代码。函数定义包含了函数声明的所有信息,并加上了函数体

    //Stack.h 声明
    struct Stack
    {int* a;int size;int capacity;//...
    };
    void StackInit(struct Stack* ps,int n=4);//*注意 必须在声明中给出缺省值
    void StackPush(struct Stack* ps,int x);
    //Stack.cpp 定义
    void StackInit(struct Stack* ps,int n)//*注意声明和定义中缺省值不能同时给
    {ps->a=(int*)malloc(sizeof(int)*n);
    }
    void StackPush(struct Stack* ps , int x)
    {}
    //Test.cpp
    #include"Stack.h"
    int main()
    {struct Stack st1;// 1、确定要插入100个数据StackInit(&st1, 100);  // call StackInit(?)//此时包含了头文件,Test.cpp只有函数声明 用这个函数的名字找到该函数的地址 编译阶段会检查调用该函数是否存在匹配的函数,经过检查 匹配// 2、只插入10个数据struct Stack st2;StackInit(&st2, 10);   // call StackInit(?)// 3、不知道要插入多少个 struct Stack st3;StackInit(&st3);return 0;
    }

    但是试想一下,1️⃣如果缺省值只在函数定义中给出,编译阶段 无法用这个函数的名字找到该函数的匹配 ,因为调用传参跟函数声明并不匹配。另一种情况,2️⃣如果在函数的声明和定义中都指定了缺省参数编译器也可能不确定应该使用哪个版本的默认值为了避免这种情况,C++标准规定了缺省参数应当只在一个地方指定:

    • 如果函数声明在头文件中进行,那么就在头文件中的声明处指定缺省参数

    • 如果函数没有在头文件中声明(例如,完全在一个.cpp文件内定义),那么就在函数定义处指定缺省参数

    综上,

    1️⃣在项目中,声明和定义应当分离,缺省值一定要在函数声明中给出!因为,编译阶段只有函数声明,从而保证编译阶段是没有问题的。

    2️⃣声明和定义分离,导致编译阶段无法找到函数的定义,没有函数的地址。

  • 再来分析上述程序

    • 理解编译与链接的过程

      1️⃣预处理阶段 :展开头文件、宏替换、条件编译、删除注释

      对于每个.c文件,编译过程从预处理开始。预处理器会处理以#开头的指令,例如#include "stack.h"会将stack.h中的内容文本上粘贴到stack.ctest.c文件中,这样stack.ctest.c就可以看到这些函数声明了

      2️⃣编译:检查语法➡️生成汇编代码

      编译器接着编译每个.c源文件,将它们转换成目标代码(通常是机器代码的一种中间形态,称为目标文件,扩展名为.o或.obj)。此时,编译器确保源代码符合语法规则,对每个源文件进行类型检查,确保所有函数调用都符合其声明,但还不解决跨文件的函数引用问题。例如,stack.c被编译成stack.o,test.c被编译成test.o

      3️⃣汇编:汇编代码➡️二进制机器码

      4️⃣链接:合并、有些地方要用函数名去其他文件找函数地址

      一旦所有的源文件被编译成目标文件,链接器(linker)负责将这些目标文件以及必要的库文件链接成一个单一的可执行文件。在链接过程中,如果test.c(对应的是test.o)调用了stack.c中(对应的是stack.o)的函数,链接器负责“修补”这些调用,使得test.o中的调用可以正确地连接到stack.o中定义的函数上,链接器确保所有外部引用都能正确解析到它们所引用的实体。

    • 理解函数与文件的关系

      • 在stack.h中声明的函数,让其他源文件知道这些函数的存在、它们的参数以及返回值类型。stack.h扮演了接口的角色。

      • stack.c提供了stack.h中声明的函数的具体实现。test.c作为使用这些函数的客户端代码,通过#include "stack.h"能够调用这些函数。

      • 编译过程中,test.c和stack.c分别被编译成中间的目标文件。这些目标文件中的函数调用尚未解析到具体的地址

      • 在链接过程,链接器解析这些调用,使得从test.o中的调用可以正确地定位到stack.o中的函数定义,从而生成一个完整的可执行文件,所有的函数调用都被正确地解析和连接,这个地址修正的过程也叫做重定位

五、函数重载

C语言不允许同名函数

C++允许同名函数。要求:函数名相同,参数不同,构成 函数重载

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

代码示例:

#include<iostream>using namespace std;// 1、参数类型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}// 2、参数个数不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}// 3、参数类型顺序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}
int main()
{Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, 'a');f('a', 10);return 0;
}

C语言不支持重载 链接时,直接用函数名去找地址,有同名函数的情况则区分不开。

  • C++是如何支持函数重载的?

    通过函数名修饰实现的,只要函数参数不同,函数名就会被修饰成不同。然后直接用修饰好的名字,去找该函数的地址。

    • 函数名修饰

      名字修饰是编译器自动进行的一种处理过程,它将C++源代码中的函数名和变量名转换成包含更多信息的唯一标识符。这些信息通常包括函数的参数类型、参数数量等,甚至可能包括所属的类名(对于类成员函数),通过这种方式,每个重载的函数都会被赋予一个独一无二的名字,确保链接器在最后链接程序的时候能够区分它们

Linux下g++的修饰规则简单易懂,下面我们使 用了g++演示了这个修饰后的名字。 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度 +函数名+类型首字母】。

  • 采用C语言编译器编译后结果

 

结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

  • 采用C++编译器编译后结果

 

结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

通过以上这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修 饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/19963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法 | 堆篇】JAVA实现小顶堆

1. 堆的特点 堆的逻辑结构是数组&#xff0c;内存结构是完全二叉树.完全二叉树即只有最后一层才有叶子节点.堆又分为大顶堆与小顶堆. 大顶堆的特点是 : 父亲节点比孩子节点的都要大. 小顶堆的特点与其相反.Java的优先级队列(PriorityQueue)的底层实现即用到了小顶堆. 所以下文…

K210视觉识别模块学习笔记3:内存卡写入拍摄图片_LED三色灯的操作_按键操作_定时器的配置使用

今日开始学习K210视觉识别模块: LED三色灯的操作_按键操作_定时器的配置使用_内存卡写入拍摄图片 亚博智能的K210视觉识别模块...... 本文最终目的是编写一个按键拍照的例程序&#xff1a; 为以后的专用场景的模型训练做准备&#xff0c;因为训练自己的模型需要大量的图片&a…

jmeter基础入门练习题

jmeter存在A,B两个线程组的情况下&#xff0c;默认设置下&#xff0c;运行顺序是&#xff1a;A A&#xff1a;A,B同时运行 B&#xff1a;先运行A&#xff0c;在运行B C&#xff1a;先运行A&#xff0c;等待2s运行B D:先A运行完&#xff0c;等待默认设置时间后运行B 下列说法正…

编译安装PHP服务(LAMP3)

目录 1.初始化设置&#xff0c;将安装PHP所需软件包传到/opt目录下 &#xff08;1&#xff09;关闭防火墙 &#xff08;2&#xff09;上传软件包到/opt目录 2.安装GD库和GD库关联程序&#xff0c;用来处理和生成图片 3.配置软件模块 4.编译及安装 5.优化把PHP 的可执行程…

nginx的安装001

Nginx是一款高性能的HTTP和反向代理服务器&#xff0c;以及邮件代理服务器&#xff0c;由 Igor Sysoev 开发并公开发布于2004年。Nginx以其高并发处理能力、低内存消耗和稳定性著称&#xff0c;特别适合部署在高流量的网站上。 操作系统&#xff1a; CentOS Stream 9 安装步骤…

【算法训练 day44 分割等和子集】

目录 一、分割等和子集-LeetCode 416思路实现代码1.二维dp代码2.一维dp代码 问题总结 一、分割等和子集-LeetCode 416 Leecode链接: leetcode 416 文章链接: 代码随想录 视频链接: B站 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&…

SQL入门教程,很详细

SQL&#xff08;Structured Query Language&#xff09;是一种用于管理关系数据库的标准语言。它被广泛用于存储、操作和检索数据。在这篇文章中&#xff0c;我们将介绍SQL的基本概念和常用命令。 首先&#xff0c;我们需要了解SQL的基本结构。SQL语句通常由以下几个部分组成&…

头歌数据结构与算法课程设计易-算式运算的合法性

给定一个算式运算&#xff0c;算式由运算数、、-、、/、(、)组成&#xff0c;请编写程序判断该算式运算是否合法。如果合法&#xff0c;计算该算式的值。 输入描述&#xff1a; 第一行输入一个运算表达式 输出描述&#xff1a; 如果表达式合法则计算其值&#xff0c;结果保留两…

c语言之向文件读写数据块

c语言需要向文件读写数据块需要用到fread语句和fwrite语句 fread语句的语法格式 fread(butter,size,count,fp) butter&#xff1a;读取的数据存入内存地址 size:读取的字节大小 count:读取数据的个数 fp:读取的文件指针 fwrite语句语法格式 fwrite(butter,size,count,fp…

企业如何利用社交媒体二维码做宣传?提升品牌形象

和普通的二维码不同&#xff0c;社交媒体二维码可以通过一个二维码链接企业的超过16的社交媒体渠道链接&#xff0c;包括&#xff1a;企业官网、小程序、公众号、淘宝店铺、抖音链接、小红书链接、美团链接、饿了么链接…等等。扫描之后&#xff0c;可以在这个社交媒体二维码界…

校园志愿者|基于SprinBoot+vue的校园志愿者管理系统(源码+数据库+文档)

校园志愿者管理系统 目录 基于SprinBootvue的校园志愿者管理系统 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2管理员功能 3志愿者功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&a…

采购订单审批和取消例子

文章目录 1 Introduction2 Example 1 Introduction This is a exmaple for releaseing po and reseting po. 2 Example DATA:lw_in TYPE zmms015,lw_out TYPE zmms015_out,lt_head LIKE TABLE OF ZMMT003_head,lw_head TYPE ZMMT003_head,lt_item TYPE zmmt003_item_t,lt…

12.RedHat认证-Linux文件系统(下)

12.RedHat认证-Linux文件系统(下) swap虚拟内存 我加一个硬盘做实验sdc # 创建交换分区&#xff08;不用做成逻辑卷也能灵活分区&#xff09; [rootcentos8 ~]# fdisk /dev/sdc -l Disk /dev/sdc&#xff1a;10 GiB&#xff0c;10737418240 字节&#xff0c;20971520 个扇区 …

REX 521馈线保护继电器提供 您的高效中压网络 保护、测量、监控和基本 控制功能

REX 521馈线保护继电器提供 您的高效中压网络 保护、测量、监控和基本 控制功能。典型的REX 521应用包括输入和输出馈线 在隔离中性点中&#xff0c;谐振接地&#xff0c;牢固 接地和电阻接地系统。 …完善ABB继电器解决方案系列 这种最先进的保护继电器补充了ABB的一系列解决方…

深入理解linux文件系统与日志分析

深入理解linux文件系统与日志分析 linux文件系统: 文件是存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区&#xff0c;每个扇区的大小是512字节。 inode&#xff1a;元信息&#xff08;文件的属性 权限&#xff0c;创建者&#xff0c;创建日期等等&#xff09; block…

【AVL Design Explorer DOE】

AVL Design Explorer DOE 1、关于DOE的个人理解2、DOE参考资料-知乎2.1 DOE发展及基本类型2.2 DOE应用场景2.3 Mintab 中的 DOE工具3、AVL Design Explorer DOE示例 1、关于DOE的个人理解 仿真和试验一样&#xff0c;就像盲人摸象&#xff0c;在不知道大象的全景之前&#xff…

Java 垃圾回收

一、概述 GC GC(Garbage Collection)&#xff0c;在程序运行过程中内存空间是有限的&#xff0c;为了更好的的使用有限的内存空间&#xff0c;GC会将不再使用的对象清除然后将其所占用的内存释放出来。 java的垃圾回收机制 Java的垃圾收集&#xff08;Garbage Collection, …

嵌入式Linux复制剪切删除指令详解

指令操作 1. cp 复制指令 a. 用法&#xff1a;cp [ 选项 ] [ 源文件或目录 ] [ 目标文件或目录 ]&#xff1b; b. 用途&#xff1a;用于复制文件或目录&#xff1b; c. 通常情况下&#xff0c;复制的都不是空文件夹&#xff0c;所以直接使用 cp 复制空文件会失败&#xff0…

创建Django项目及应用

1 创建Project 1个Project可以对应多个app django-admin startproject myproject 2 创建App python manage.py startapp app01 INSTALLED_APPS [# ...app01,app02,# ... ] 如果要让这个应用在项目中起作用&#xff0c;需要在项目的 settings.py 文件的 INSTALLED_APPS 配置…

java中成员内部类、局部内部类、匿名内部类各自的特点

成员内部类&#xff1a;定义在类的内部&#xff0c;方法的外部&#xff0c;成员内部类作为外部类的成员&#xff0c;可以直接访问外部类的私有属性。 局部内部类&#xff1a;定义在方法的内部&#xff0c;对于局部内部类我们常常使用一个方法&#xff0c;得到一个接口实现类的…