当前位置: 首页 > news >正文

C/C++(二)C++入门基础

这一章会介绍C++入门必须掌握的一些基础概念

一、函数重载

1、什么是函数重载?

函数重载是C++相比于C语言的一个重大改进。

C++允许在同一作用域内声明多个功能类似的同名函数,这些函数的参数类型 / 个数 / 类型顺序不同。(注:返回值不同不能构成重载,这是由后文会介绍的函数名修饰规则所决定)

2、C++如何支持函数重载的?(Linux g++编译器环境下)

在C/C++(一)中介绍了,在C / C++中,想要形成一个可执行程序,必须经历 预处理 --> 编译 --> 汇编 --> 链接 四个阶段。

在编译阶段,g++ 编译器会根据其函数名修饰规则(_Z + 函数长度 + 函数名 + 函数参数类型的首字母)(可以发现,修饰规则中没有返回值,因此返回值不同,不能构成函数重载)对函数进行修饰,根据其规则可以看出,函数参数不同,实际上在编译阶段会被修饰成不同的函数这也就是C++支持函数重载的根基。

示例代码:

#include <iostream>
using namespace std;int add(int x, int y)
{return x + y;
}
int add(int x, int y, int z)
{return x + y + z;
}
int main()
{int res1 = add(1, 2);int res2 = add(1, 2, 3);
}

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

因为C语言的编译器的函数名修饰规则并不会修饰函数名,而是按照原先函数名。 

二、引用和指针

1、什么是引用?

引用是给已经存在的变量取别名, 编译器不会为引用开辟空间,引用和其引用的变量共用同一块内存空间

使用格式
 

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

1.1  有关引用的一些注意事项

1、& 跟在类型和变量名之间,叫做引用。否则,叫做取地址。

2、引用必须在定义时就初始化。

3、一个变量可以有多个引用,引用本身也可以有引用。

4、引用一旦引用了一个实体,就不能再引用其它实体。

5、非常量应用不能绑定到常量对象 / 临时常量对象 /不同类型的对象;常量引用可以绑定到        常量对象、临时常量对象或不同类型的对象(需要显式类型转换)

1.2  常引用(常量引用 / const 引用)

常引用是引用的一种特殊形态,允许引用一个对象,可以绑定到常量对象 / 临时常量对象 / 不同类型的对象(需要显式类型转换),但是不能通过该引用修改对象的值,主要用途是提高代码的安全性和可读性。

示例代码:

void TestConstRef()
{const int a = 10;// int& ra = a;       // 该语句编译时会出错,a为常量,普通引用不能绑定常量对象const int& ra = a;    // 正确方法// int& b = 10;       // 该语句编译时会出错,10为临时常量,const int& b = 10;    // 正确方法double d = 12.34;//int& rd = d;        // 该语句编译时会出错,类型不匹配(具体原因是由于类型转换时会产生临时变 // 量,而临时变量具有常性造成无法引用)const int& rd = static_cast<int>(d);    // 正确方法1double& rd = d                          // 正确方法2
}

2、指针和引用的区别(用法 + 底层原理)

1、引用是定义一个变量的别名,与这个变量共用同一内存空间,大小也与这个变量的大小          相同;而指针存储的是变量的地址,需要额外占用一小段空间储存,大小也始终是指针          的大小。(32位下是4字节、64位下是8字节)

2、引用必须在声明的时候就进行初始化,不能为空,并且一旦初始化就不能改变引用的对          象(因为要让编译器知道,引用总是指向这个有效的对象,以便生成更高效的代码,              直接访问目标变量的内存地址,而不需要额外的解引用操作);指针由于通过解引用显          式访问变量,所以可以在声明的时候不初始化,也可以随时改变指向的对象。

3、引用++就相当于其引用的变量++;指针则是指针指向下一个内存地址。

4、引用访问绑定的实体,直接访问,由编译器来优化而指针需要解引用。

5、因为引用不能为空 + 编译器优化,引用比指针更安全。

3、引用的两大使用场景 

三、宏的替代方案

宏作为从C语言继承而来的概念(C / C++(一)中有详细介绍),其有着一定的优点(代码复用性强;可以提高性能),但是其缺点更多(由于预编译阶段进行了替换,宏不方便调试;代码的可读性较差,可维护性也不好,还容易误用;也没有类型安全的检查)

所以C++提供了三种用来替代宏的方案

替代宏定义标识符:const变量 / enum枚举

替代宏函数:inline内联函数

1、const变量替代宏定义标识符

// 宏定义标识符
#define PI 3.14159// const变量定义
const double PI = 3.14159;

优点:

1、类型安全const 变量具有明确的类型,编译器可以进行类型检查,避免类型错误。

2、调试友好const 变量在调试器中可以显示其值和类型,便于调试。

3、作用域控制const 变量可以有局部作用域,而宏定义在整个文件范围内有效,容易引起                             命名冲突。

2、enum枚举替代宏定义标识符

// 宏定义标识符
#define RED 0
#define GREEN 1
#define BLUE 2// 枚举定义标识符
enum Color 
{RED,GREEN,BLUE
};

优点:

1、类型安全enum 类型的变量只能取枚举值,方便编译器进行类型检查。

2、语义清晰enum 明确表示一组相关的常量,代码更具可读性。

3、范围限制:枚举值在编译时有固定的范围,避免了非法值的赋值。

3、inline 内联函数替代宏函数

3.1  什么是内联函数?

以 inline 关键字修饰的函数叫做内联函数,

特性:在编译的时候,C++编译器会在调用内联函数的地方直接用函数体替换,没有调用普             通函数需要建立栈帧的额外开销,可以提升程序运行的效率

缺点:以空间换时间,可能会导致目标文件变大。(因此,内联 inline 关键字实际上只是向             编译器发出的一个内联请求,编译器会自己决定是否把这个函数设置为内联函数,如            果函数过长,编译器将忽略内联请求)

使用建议:

1、函数规模较小、不是递归,调用不频繁的函数,可以采用 inline 内联修饰;其它                      的不建议;

2、同时也不建议内联函数的声明与定义分离,因为分离了,每个cpp文件里都会有内联函数        的内联展开,链接器会发现不同的对象文件中都有这个内联函数的定义。无法确定应该          使用哪个定义,导致多重定义错误。(如果一定要使用,内联函数的声明和定义必须放           在同一个地方,通常是头文件中,确保每个编译单元都能看到完整的内联函数定义)

3.2  以内联函数替代宏函数

#define MAX(a, b) ((a) > (b) ? (a) : (b))

// #define 定义宏
#define MAX(a, b) ((a) > (b) ? (a) : (b))// inline内联函数代替宏函数
inline int max(int a, int b) 
{return (a > b) ? a : b;
}

优点:

1、类型安全inline 函数具有明确的参数类型和返回类型,编译器可以进行类型检查。

2、副作用避免:宏定义在参数计算上有潜在的副作用,而 inline 函数没有这个问题。

3、调试友好inline 函数在调试器中可以显示调用栈和局部变量,而宏定义则不行。

4、代码可读性inline 函数的代码更具可读性和可维护性。

四、新的空指针表示方式:nullptr

nullptr是C++11引入的一种新的空指针表示方式在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

在传统的C语言 / C++98标准中,空指针的表示方式一般是NULL,而NULL的定义存在缺陷

NULL的缺陷

NULL在在 C++ 中,通常定义为 0 或 (void*)0,这可能导致一些意外的行为

void f(int)
{cout<<"f(int)"<<endl;
}
void f(int*)
{cout<<"f(int*)"<<endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
}

这段代码的本意是希望想通过 f(NULL) 调用指针版本的 f(int*) 函数,但是由于NULL被定义成0,因此与程序的初衷相悖。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,还必须对其进行强转 (void *)0。

nullptr的优势

1、类型安全nullptr 是一个类型,避免了与整数类型的隐式转换。

2、更好的可读性nullptr 明确表示这是一个空指针,且不需要包含其它头文件。

3、兼容性更好nullptr 可以更好地与模板一起使用。

4、避免宏定义的问题nullptr 是一个关键字,不会受到宏定义的影响。

5、支持 nullptr 类型的检查:可以使用 std::nullptr_t 类型进行类型检查。

因此为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr。

http://www.lryc.cn/news/465905.html

相关文章:

  • 人工智能发展:一场从“被教导”到“自我成长”的奇妙冒险
  • 企业级 RAG 全链路优化关键技术
  • 学习文档(5)
  • node.js下载安装以及环境配置超详细教程【Windows版本】
  • 08_实现 reactive
  • finereport 中台 帆软 编码解码
  • Day15-数据库服务全面优化与PT工具应用
  • 开源限流组件分析(二):uber-go/ratelimit
  • 探索 SVG 创作新维度:svgwrite 库揭秘
  • 为什么要做PFAS测试?PFAS检测项目详细介绍
  • 稀土阻燃协效剂的应用
  • Java的异常处理
  • 免费域名邮箱申请和使用教程:有哪些步骤?
  • Linux之实战命令45:swapon应用实例(七十九)
  • 提升数据处理效率:TDengine S3 的最佳实践与应用
  • 高级算法设计与分析 学习笔记13 线性规划
  • 2024年11月软考中项应试技巧与机考注意事项!
  • 网络编程中容易踩的坑罗列,谨记!
  • SD-WAN:推动企业网络优化与发展
  • [MyBatis-Plus]扩展功能详解
  • 循序渐进丨MogDB 5.0 远程访问 MogDB/Oracle 数据库的简便方法(使用@符号)
  • 大模型训练触达「瓶颈」,基座模型厂商还有必要坚持预训练吗?
  • media3 exoplayer 扩展解码库在这里 take it , please !
  • 在Xshell中查看日志文件详情
  • 深入理解计算机系统--计算机系统漫游
  • 哪些指标可以用来评估精益生产现场管理和改善的效果?
  • 在 Linux 系统上安装免费杀毒软件
  • 第 7 章:Vue UI 组件库
  • 【SQL】SQL用户管理和权限
  • STM32应用详解(5)USART串口初始化