从C到C++,C++入门(2)

在C++入门篇(1)中,博主为大家简单介绍了什么是C++,以及C++中的关键字,命名空间,输入与输出和缺省参数的相关知识。今天就让我们继续一起学习C++的基础知识点吧!!

1.函数重载

1.1函数重载的概念

  • 在自然语言中,一个词可以有多重含义,人们可以通过上下文来判断这个词语的真实含义。举一个简单的例子:“读书好,读好书,好读书” 中三个“好”的意思是完全不同的,即该词被重载了。
  • 函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数/参数类型/类型顺序)不同,常用来处理实现功能类似但数据类型不同的问题
  • C语言是不允许同名函数的存在的
  • 但在C++里,不同作用域里的函数可以重名,同一作用域里的函数也可以重名,但要满足重载条件

 1.2函数重载的条件

参数类型不同

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;
}

参数个数不同

void f()
{
  cout<<"f()"<<endl;
}

void f(int a)
{
  cout<<"f(int a)"<<endl;
}

参数类型顺序不同

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;
}

1.3C++支持函数重载的原理

大家有没有思考过为什么C语言不支持函数重载,但是C++却支持函数重载呢?

C语言不支持函数重载是因为C语言是一种过程式编程语言,它的设计初衷是简洁高效,不包含太多复杂的特性。在C语言中,函数的标识符是唯一的,不允许同名函数存在,因此无法实现函数重载

C++支持函数重载是通过名字修饰(Name Mangling)来实现的。在C++中,当定义多个同名函数但参数列表不同的函数时,编译器会根据函数的参数类型、个数和顺序生成不同的函数名,这个过程就叫做名字修饰。

名字修饰的过程会将函数名和参数列表信息结合起来,生成一个唯一的函数名,这样就能区分不同的重载函数。在调用函数时,编译器会根据函数名和参数列表来选择调用哪个函数。

举个例子,假设我们有两个重载的函数:

void print(int num);
void print(double num);

在编译过程中,编译器会对这两个函数进行名字修饰,生成类似以下的函数名:

print_int
print_double

这样,即使这两个函数具有相同的函数名,但由于它们的参数类型不同,在生成的函数名中会包含参数类型信息,从而能够区分这两个函数。

总之,C++支持函数重载是通过名字修饰来实现的,编译器会根据函数的参数类型和个数生成不同的函数名,从而实现了函数重载的特性。

2.引用

2.1引用的概念

引用不是定义了一个新的变量,而是给已经存在的变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

比如:李逵,在家称为“铁牛”,江湖上人称“黑旋风”。别名就相当于一个人的外号

格式:类型& 引用变量名(对象名)= 引用实体

注意,引用类型必须和引用实体是同种类型的

void TestRef()
{
  int a = 10;
  int& ra=a;//定义引用类型
  printf("%p\n",&a);
  printf(%p\n",&a);//两个地址打出来是一样的
}

2.2引用的特性

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,就不能再引用其他实体
void TestRef()
{
  int a = 10;
 //int& ra;没有初始化,这条语句在编译的时候会出错
  int& ra=a;//ra是a的别名
  int& rra = a;//rra也是a的别名
  printf("%p %p %p\n",&a,&ra,&rra);
}

引用出错举例

谨记:权限可以平移和缩小,但不能放大

void TestConstRef()
{
  const int a = 10;
//int& ra=a;该语句编译时会出错,a本不可修改,现在通过ra可以修改a了,权限扩大了
  const int& ra = a;//这种是合法的
  
  double d=12.34;
//int& rd = d;该语句编译时会出错,因为类型不同
}

int main()
{
  double d = 12.34;
  //类型转换会生成临时变量,实际将临时变量给了i
  int i= d;
  int& r = d;//错误写法,因为临时变量具有常性,不能随便改,这样写导致了权限的扩大
  const int& r = d;//正确
  return 0;
}

给指针变量取别名

int* p1=&x;
int*& pr=p1;
pr= NULL;

2.3使用场景

做参数

void Swap(int& left,int& right)
{
  int temp = left;
  left = right;
  right= temp;
}

做返回值

int& Count()
{
  static int n = 0;
  n++;
  //...
  return n;
}

2.4传值传引用效率对比

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝。因此用值作为参数或者返回值类型,效率很低

2.5引用和指针的区别与联系

联系:在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。 但是在底层实现上引用是有空间的,因为引用是按照指针方式来实现的。总结:引用和指针在底层都开空间,但是引用从语法角度看不开空间,从底层看两者都开空间。

区别:

  • 引用概念上定义一个变量的别名,指针存储一个变量地址
  • 引用在定义时必须初始化,指针没有要求
  • 引用在初始化引用一个实体以后就不能再引用其他实体,然而指针可以在任何时候指向任何一个同类型实体
  • 没有NULL引用,但有NULL指针
  • 两者在sizeof中含义不同,引用结果为引用类型的大小,但是指针始终是地址所占字节个数
  • 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  • 有多级指针,但是没有多级引用
  • 两者访问实体方式不同,指针需要显示解引用,引用编译器会自己处理
  • 引用比指针使用起来相对安全一些

3.内联函数

3.1内联函数的定义

在C++中,内联函数是一种特殊的函数,它会在每个调用点上被直接插入到程序中,而不是通过函数调用的方式进行执行。这样可以减少函数调用建立栈帧的开销,提高程序的执行效率。

要定义一个内联函数,需要在函数的声明和定义前面加上关键字inline

inline int add(int a, int b) {
    return a + b;
}

 在上面的例子中,inline关键字告诉编译器将add函数作为内联函数进行处理。当函数被调用时,它的代码会被直接插入到调用点上,而不是通过函数调用的方式执行。

3.2特性

  • inline是一种以时间换空间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。
  • 好处是少了调用开销,提高程序运行效率。坏处是可能会使目标文件变大。
  • inline对于编译器而言只是一个建议,不同编译器对于inline的实现方式可能不同,编译器可以选择忽略该请求
  • 通常情况下,内联函数适合用于简单的、频繁调用的函数,这样可以减少函数调用的开销。但是对于复杂的函数或者包含循环、递归等复杂逻辑的函数,声明为内联函数可能会导致代码膨胀,反而降低程序的执行效率。
  • 在实际使用中,编译器会根据函数的复杂度和调用频率来决定是否将函数作为内联函数进行处理,因此我们在编写代码时可以根据实际情况考虑是否将函数声明为内联函数

4.auto关键字

4.1概念

在C++11中,引入了auto关键字,用于让编译器自动推导变量的类型。使用auto关键字定义变量时,编译器会根据变量的初始化表达式推导出变量的实际类型。这样可以简化代码,特别是在处理模板类型、迭代器和复杂的迭代器类型时非常有用。 

auto x = 10; // 推导x的类型为int
auto d = 3.14; // 推导d的类型为double
auto str = "Hello"; // 推导str的类型为const char*

std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    // 使用auto迭代器,编译器会自动推导it的类型为std::vector<int>::iterator
    std::cout << *it << std::endl;
}

std::map<std::string, int> wordCount = {{"hello", 1}, {"world", 1}};
for (auto& kv : wordCount) {
    // 使用auto遍历map,编译器会自动推导kv的类型为std::pair<const std::string, int>&
    std::cout << kv.first << ": " << kv.second << std::endl;
}

auto关键字并不是完全的类型推导,编译器会根据初始化表达式来推导变量的类型,因此在使用auto关键字定义变量时,一定要确保初始化表达式是明确的,否则编译器无法推导出变量的类型。

auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会把auto替换为变量实际的类型

4.2auto的使用细则

1.auto与指针和引用结合起来使用

auto声明指针类型时,auto和auto*没有任何区别,但是用auto声明引用类型时必须加&

int mian()
{
  int x = 10;
  auto a=&x;
  auto* b = &x;
  auto& c = x;
}

2.在同一行定义多个变量

在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器会报错

void TestAuto()
{
  auto a=1,b=2;
  auto c=3,d=4.0;//这行代码会编译失败,因为c和d的初始化表达式类型不一样
}

4.3不宜使用auto的场景

1.auto不能作为函数的参数

void TestAuto(auto a)
{
}
//编译失败,因为编译器无法对a的实际类型进行推导

2.auto不能直接用来声明数组

void TestAuto()
{
  int a[]={1,2,3};
  auto b[]={4,5,6};
}

5.基于范围的for循环

5.1概念

C++11引入了基于范围的for循环(range-based for loop),它提供了一种简洁的语法来遍历容器、数组和其他支持迭代器的对象的元素。基于范围的for循环可以大大简化遍历容器或数组时的代码,同时也更加直观和易读。

语法如下

for (auto element : container) {
    // 对每个元素执行操作
}

在这个语法中,container是一个支持迭代器的对象,例如数组、标准库容器(如vector、list、map等)、字符串等。element是一个迭代器指向的元素的临时变量,它的类型会根据container中元素的类型自动推导。

下面是一个示例:

std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto element : vec) {
    std::cout << element << " ";
}

在这个例子中,基于范围的for循环会遍历vec中的每个元素,并将当前元素赋值给element,然后执行循环体中的操作。这样就可以方便地遍历容器中的元素,而不必显式地使用迭代器或者索引。

需要注意的是,基于范围的for循环在遍历容器或数组时是以值的方式进行操作的,如果需要对容器中的元素进行修改,可以使用引用类型来避免值的拷贝 

for (auto& element : vec) {
    element *= 2; // 修改vec中的每个元素为原来的两倍
}

5.2使用条件

  • for循环迭代的范围必须要是确定的:对于数组而言,就是数组中的第一个元素和最后一个元素;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围
  • 迭代的对象要实现++和==的操作

6.指针空值nullptr

6.1出现的原因

在C++中,定义指针空值nullptr的主要原因是为了提供一种更明确、更安全的方式来表示指针不指向任何有效对象或内存位置。在nullptr被引入之前,程序员通常使用NULL宏或者字面值0(或(void*)0在某些上下文中)来表示空指针。但是,这些做法存在一些潜在的问题和不便。

6.2注意点

  • 使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的
  • 在C++11中,sizeof(nullptr) 与sizeof((void*)0)所占的字节数相同
  • 为了提高代码的健壮性,在后续表示指针空值的时候建议最好用nullptr

尾声

关于C++入门的相关知识到这里就告一段落了,欢迎大家在评论区提问

点赞+评论+关注 是博主持续不断更新优质好文的最大动力!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C# WPF入门学习主线篇(十九)—— 布局管理实战『混合布局案例』

C# WPF入门学习主线篇&#xff08;十九&#xff09;—— 布局管理实战『混合布局案例』 欢迎来到C# WPF入门学习系列的第十九篇。在前几篇文章中&#xff0c;我们详细介绍了各个布局容器的基本概念和使用方法。本篇博客将通过一个综合的实战案例&#xff0c;展示如何在WPF中使用…

Comfyui容器化部署与简介

目前使用 Stable Diffusion 进行创作的工具主要有两个&#xff1a;Stable Diffusion WebUI 和 ComfyUI。本文重点介绍ComfyUI的部署使用。 ComfyUI 可定制性很强&#xff0c;可以让创作者搞出各种新奇的玩意&#xff0c;通过工作流的方式&#xff0c;也可以实现更高的自动化水平…

k8s学习--kubernetes服务自动伸缩之水平收缩(pod副本收缩)VPA详细解释与安装

文章目录 前言VPA简介简单理解详细解释VPA的优缺点优点1.自动化资源管理2.资源优化3.性能和稳定性提升5.成本节约6.集成性和灵活性 缺点1.Pod 重启影响可用性2.与 HPA 冲突3.资源监控和推荐滞后&#xff1a;4.实现复杂度&#xff1a; 核心概念Resource Requests 和 Limits自动调…

【MySQL】(基础篇三) —— 创建数据库和表

管理数据库和表 管理数据库 创建数据库 在MySQL中&#xff0c;创建数据库的SQL命令相对简单&#xff0c;基本语法如下&#xff1a; CREATE DATABASE 数据库名;如果你想避免在尝试创建已经存在的数据库时出现错误&#xff0c;可以添加 IF NOT EXISTS 子句&#xff0c;这样如…

AI 边缘计算平台 - 6 TOPS 低功耗 RK3576

RK3576 是瑞芯微第二代 8nm 高性能 AIOT 平台&#xff0c;CPU 采用八核大小核构架&#xff08;4A72 2.2GHz 4A53 1.8GHz&#xff09;&#xff0c;以及一个 M0 协处理器。其 CPU 算力高达 58K DMIPS&#xff0c;足以应对各种复杂计算任务。搭载 Mali-G52 MC3 GPU&#xff0c;14…

vscode软件上安装 Fitten Code插件及使用

一. 简介 前面几篇文章学习了 Pycharm开发工具上安装 Fitten Code插件&#xff0c;以及 Fitten Code插件的使用。 Fitten Code插件是是一款由非十大模型驱动的 AI 编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试 Bug&#xff0c;节省…

【CS.AI】GPT-4o:重新定义人工智能的新标杆

文章目录 1 序言2 GPT-4o的技术亮点3 GPT-4o与前代版本的对比3.1 热门AI模型对比表格GPT-3.5GPT-4GPT-4oBERTT5 3.2 其他 4 个人体验与感受5 结论 1 序言 嘿&#xff0c;大家好&#xff01;今天要聊聊一个超级酷的AI新突破——GPT-4o&#xff01;最近&#xff0c;OpenAI发布了…

【报文数据流中的反压处理】

报文数据流中的反压处理 1 带存储体的反压1.1 原理图1.2 Demo 尤其是在NP芯片中&#xff0c;经常涉及到报文的数据流处理&#xff1b;为了防止数据丢失&#xff0c;和各模块的流水处理&#xff1b;因此需要到反压机制&#xff1b; 反压机制目前接触到的有两种&#xff1a;一是基…

ARM功耗管理框架之SCP

安全之安全(security)博客目录导读 目录 一、功耗管理框架中的SCP 二、SCP的示例 三、SCP固件 四、SCP启动流程 五、SCP的memory map 六、SCP与AP的通信 思考&#xff1a;功耗管理框架&#xff1f;SCP&#xff1f;PPU&#xff1f;LPI&#xff1f;之间的关系&#xff1f…

(三)React事件

1. React基础事件绑定 语法&#xff1a; on 事件名称 { 事件处理程序 }&#xff0c;整体上遵循驼峰命名法 App.js //项目根组件 //App -> index.js -> public/index.html(root)function App() {const handleClick () > {console.log(button被点击了)}return (<…

测试开发之自动化篇 —— 使用Selenium IDE录制脚本!

今天&#xff0c;我们开始介绍基于开源Selenium工具的Web网站自动化测试。 Selenium包含了3大组件&#xff0c;分别为&#xff1a;1. Selenium IDE 基于Chrome和Firefox扩展的集成开发环境&#xff0c;可以录制、回放和导出不同语言的测试脚本。 2. WebDriver 包括一组为不同…

ATTCK红队评估(五)

环境搭建 靶场拓扑图&#xff1a; 靶机下载地址: 漏洞详情 外网信息收集 确定目标靶机地址&#xff1a; 发现主机192.168.135.150主机是本次攻击的目标地址。探测靶机开放的端口信息&#xff1a; 目标靶机开放了两个端口&#xff1a;80、3306&#xff0c;那没什么意外的话就是…

企业如何运用信息化、智能化、数字化等技术手段规避企业合同风险?

在企业运营中&#xff0c;合同管理是至关重要的一环。它涉及到企业的各个方面&#xff0c;从供应链管理到客户关系&#xff0c;从财务交易到法律合规。然而&#xff0c;传统的合同管理方式往往存在效率低下、风险控制不足等问题。 随着信息化、智能化和数字化技术的发展&#…

go语言后端开发学习(一)——JWT的介绍以及基于JWT实现登录验证

什么是JWT JWT,全名为JSON Web Token&#xff0c;是当下主流的一种服务端通信认证方式&#xff0c;具有轻量,无状态的特点&#xff0c;它实现了让我们在用户与服务器之间传递安全可靠的Json文本信息&#xff0c;它的使用过程主要是这样的&#xff1a; 当用户注册的时候&#x…

Linux——nginx部署

部署Nginx 构建Nginx服务器 &#xff08;实验需要DNS支持&#xff0c;或添加hosts条目&#xff0c;例如&#xff1a; &#xff09; 安装Nginx&#xff08;yum安装即可&#xff09; 安装依赖软件包&#xff1a; 重启、启用服务并查看服务状态&#xff1a; 默认页面&#xff0…

【深度学习】深度学习之巅:在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境

【深度学习】深度学习之巅&#xff1a;在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境 大家好 我是寸铁&#x1f44a; 总结了一篇【深度学习】深度学习之巅&#xff1a;在 CentOS 7 上打造完美Python 3.10 与 PyTorch 2.3.0 环境✨ 喜欢的小伙伴可以点点关注 &#…

模糊控制器实现对某个对象追踪输入

MATLAB是一个十分便捷的软件&#xff0c;里面提供了许多集成的组件&#xff0c;本文利用simulink实现模糊控制器实现对某个对象追踪输入。 这里的对象根据自己的需求可以修改&#xff0c;那么搭建一个闭环控制系统并不是难事儿&#xff0c;主要是对于模糊控制器参数的设置&…

海思SD3403,SS928/926,hi3519dv500,hi3516dv500移植yolov7,yolov8(21)Yolov9s测试

四天前yolov9的作者终于开源了yolov9s和yolov9t模型。这个作者之前一直没开源t,s,只有c开始的,而且onnx转换后数据大小特别大,当时直接就放弃测试了。 另外之前代码有很明显的抄v5的痕迹。所以印象很不好。 现在总算是开源t,s模型,而且这里评估的结果上来看是好于yolov8的…

IDEA创建Mybatis项目

IDEA创建Mybatis项目 第一步&#xff1a;创建库表 -- 创建数据库 create database mybatis_db;-- 使用数据库 use mybatis_db;-- 创建user表 CREATE TABLE user (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(50) NOT NULL,password VARCHAR(50) NOT NULL,email VARC…

Django API开发实战:前后端分离、Restful风格与DRF序列化器详解

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff…