C++中函数的特性

文章目录

    • 一、C++中形参带默认值的函数
    • 二、C++中的inline内联函数
    • 三、C++中的inline内联函数和普通函数的区别
    • 四、C++中函数重载
    • 五、C++为什么支持函数重载
    • 六、C语言为什么无法实现函数重载

一、C++中形参带默认值的函数

在C++中,形参带默认值的函数是指在函数声明或定义时,为某些参数提供默认值。如果在调用函数时没有为这些参数传递实际值,就会使用默认值。
以下是关于默认参数的详细说明和示例:
1.默认参数的定义和使用
语法:

返回类型 函数名(参数类型 参数名 = 默认值, ...);

示例:

#include <iostream>// 函数声明时提供默认参数
void greet(const std::string& name = "Guest", int times = 1);int main() {greet();                       // 使用所有默认值greet("Alice");                // 覆盖第一个参数默认值greet("Bob", 3);               // 覆盖所有默认值return 0;
}// 函数定义
void greet(const std::string& name, int times) {for (int i = 0; i < times; ++i) {std::cout << "Hello, " << name << "!" << std::endl;}
}

2.形参带默认值的函数的使用规则和注意事项
默认参数从右向左设置:

默认参数必须从最后一个参数开始依次向前定义。
如果某个参数设置了默认值,则其右侧所有参数也必须有默认值。
错误示例:

void foo(int a = 10, int b); // 编译错误

默认参数只需声明一次:
默认参数通常出现在函数声明中,而不应在定义中重复。
正确示例:

void foo(int a = 10);  // 声明中定义默认值
void foo(int a) {      // 定义中无需再写默认值std::cout << a << std::endl;
}

默认参数与函数重载:
默认参数会影响函数重载的选择,可能导致二义性。
示例:

void print(int a, int b = 5);
void print(int a);print(10); // 二义性,无法确定调用哪个函数

默认参数与指针/引用:
可以为指针或引用类型设置默认参数,但需注意默认值的生命周期。

void display(const int* ptr = nullptr);

二、C++中的inline内联函数

在C++中,inline是一种修饰符,建议编译器将函数的调用替换为函数体的代码,以减少函数调用的开销(如函数跳转、参数压栈等)。这对那些体积小、频繁调用的函数特别有用。
1.内联函数的特性

  • 编译器建议而非强制:inline只是向编译器提出建议,编译器可能会忽略inline关键字(例如函数体过大或复杂时),优化行为由编译器决定,而非程序员强制控制。
  • 减少函数调用开销:减少了函数调用时的跳转和栈操作的开销。提高运行效率,但可能导致代码膨胀(代码段增大)。
  • 适合体积小的函数:内联函数适用于简单函数,例如访问器(getter)或工具函数。
  • 必须在定义处可见:内联函数的定义需要在每个调用点可见(通常放在头文件中)。
  • 内联和递归:内联函数不能有效处理递归,因为递归会导致无限展开。

2.内联函数的声明与定义
语法:

inline 返回类型 函数名(参数列表) {// 函数体
}

示例:

#include <iostream>// 内联函数定义
inline int add(int a, int b) {return a + b;
}int main() {int result = add(3, 5);  // 编译器可能直接将 add(3, 5) 替换为 "3 + 5"std::cout << "Result: " << result << std::endl;return 0;
}

3.内联函数的优势

  • 减少函数调用开销:常规函数调用需要跳转到函数地址,并将参数压栈,内联函数通过代码替换避免了这些开销。
  • 提高程序执行效率:减少了运行时的跳转操作,尤其是在频繁调用的小函数场景下。
  • 允许编译器优化:内联展开后,编译器可以进一步优化代码,例如移除不必要的中间变量。

三、C++中的inline内联函数和普通函数的区别

1.定义方式不同
内联函数是通过在函数声明或定义前加inline关键字,或定义在类体内的函数默认是内联函数。

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

普通函数没有inline关键字修饰。

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

2.调用方式不同
编译器尝试将内联函数的调用展开为函数体,直接替换到调用点,而不需要进行函数调用的开销(如参数压栈、跳转等)。
内联展开后:

int result = add(3, 5); // 可能被替换为:int result = 3 + 5;

在程序运行时执行普通函数的调用时,跳转到函数地址,完成参数传递和返回值的处理。每次调用函数时需要进行参数压栈、跳转、返回等操作,带来一定的运行时开销。

3.虚函数支持

内联函数不能直接用于虚函数,因为虚函数通过动态绑定在运行时确定调用目标,无法在编译时展开,因此通常不能内联。但若通过具体类对象调用虚函数(非通过多态指针或引用),编译器可能将其优化为内联。
普通函数支持虚函数的所有特性,无需特殊处理。

四、C++中函数重载

函数重载(Function Overloading)是C++的一项特性,允许在同一个作用域中定义多个函数名相同但参数列表不同的函数。编译器根据调用函数时提供的参数类型和数量,自动选择匹配的函数版本。
函数重载的特点:

1.函数名相同:所有重载的函数必须有相同的名字。
2.参数列表不同:可以通过:参数的数量不同、参数的类型不同,参数的顺序不同(对于不同类型的参数)等任意一种方式来区分。
3.返回值类型不能用来区分重载:仅靠返回值类型的不同不能构成重载,因为调用时返回值通常不影响函数的匹配。

函数重载的基本语法:

#include <iostream>// 重载函数
void display(int num) {std::cout << "整数: " << num << std::endl;
}void display(double num) {std::cout << "浮点数: " << num << std::endl;
}void display(const std::string& text) {std::cout << "字符串: " << text << std::endl;
}int main() {display(42);            // 调用第一个重载函数display(3.14);          // 调用第二个重载函数display("Hello!");      // 调用第三个重载函数return 0;
}

函数重载的注意事项:
1.默认参数与重载冲突
如果某些函数参数有默认值,可能与其他重载函数冲突,导致编译错误。例如:

void func(int a, int b = 10); // 带默认参数
void func(int a);             // 重载函数

调用func(5)时,编译器无法确定是调用哪个版本。

2.避免模糊调用
当调用函数时,编译器无法确定最佳匹配的重载版本时会报错。例如:

void func(double a);
void func(int a);func(3.0f); // 编译器可能报错,因为float可以转换为double或int

3.与类型转换结合使用
如果存在隐式类型转换,可能会导致意外的重载选择。例如:

void func(int a);
void func(double a);func('A'); // 'A' 被隐式转换为int,调用func(int)

五、C++为什么支持函数重载

C++支持函数重载的原理主要依赖于编译器在编译时对函数名的修饰(Name Mangling)和重载解析机制,使得同名但参数列表不同的函数能够共存于同一个作用域中。
1.函数重载的实现原理
(1) 名称修饰(Name Mangling)

在C++中,函数名本身不足以唯一标识一个函数,因此编译器通过名称修饰,将函数名与其参数列表的类型信息组合,生成唯一的符号名。

源代码中:

void func(int a);
void func(double b);
void func(int a, double b);

在编译后,这些函数可能被修饰为以下符号(不同编译器略有不同,但思想一致):

  • func(int) → _Z4funci
  • func(double) → _Z4funcd
  • func(int, double) → _Z4funcid

调用func(5)时,编译器根据传入的参数类型查找匹配的符号,例如_Z4funci。
名称修饰编码包括:

函数名。
参数类型:参数的顺序、类型、数量均被编码。
作用域信息:如命名空间或类作用域。

注意:返回值不参与修饰:因为返回值类型不会影响函数调用时的匹配。

(2) 编译器的重载解析
编译器根据函数调用时的实参信息进行解析,选择最匹配的函数版本:

参数数量:匹配参数数量相同的函数。
参数类型:精确匹配优先。如果没有精确匹配,尝试进行隐式类型转换(如从int到double)。
匹配优先级:编译器按从高到低的优先级寻找匹配函数:

  • 完全匹配(无需类型转换)。
  • 标准类型转换(如int到float)。
  • 用户定义的类型转换(通过转换构造函数或转换运算符)。
  • 可变参数(如…)。

示例:

void func(int);
void func(double);int main() {func(5);       // 调用func(int)func(3.14);    // 调用func(double)func('A');     // 'A' 被转换为int,调用func(int)
}

如果存在多个候选函数且无法决定最佳匹配,编译器将报“调用模糊”错误。

2. 函数重载的技术基础
(1) 参数类型信息的区分
函数重载的核心在于“参数类型信息”的差异性,具体包括:

参数数量不同:例如func(int)和func(int, int)。
参数类型不同:例如func(int)和func(double)。
参数顺序不同:例如func(int, double)和func(double, int)。

(2) 与默认参数的结合
默认参数是函数重载的一个特殊情况。如果两个重载函数的参数列表在默认值的影响下出现歧义,编译器会报错。

void func(int a, int b = 10); // 带默认参数
void func(int a);             // 重载函数
func(5); // 调用模糊:无法确定调用哪个版本

(3) 隐式类型转换的影响
如果参数列表中允许类型转换,可能会导致意外的重载选择。例如:

void func(int);
void func(double);func(5.0f); // 5.0f(float) 可以转换为 int 或 double,可能导致调用冲突

这种情况下,可能需要显式指定调用的版本。

3.函数重载的编译过程
编译器的具体步骤:

符号生成(Name Mangling):编译器对每个函数的名字和参数列表进行编码,生成唯一的符号名称。
重载解析:编译器在调用函数时,根据调用时提供的参数类型和数量,在符号表中寻找最匹配的重载函数。
类型转换检查:如果没有直接匹配的函数,编译器尝试通过隐式类型转换找到合适的重载函数。
代码生成:一旦找到合适的函数版本,编译器生成对应的机器代码。

4.实现的边界与限制
返回值类型不影响重载,仅靠返回值类型的不同无法实现函数重载。这是因为调用时返回值通常未显式声明,编译器无法根据返回值来区分。

六、C语言为什么无法实现函数重载

1.函数命名规则的限制
在C语言中,函数的名字本质上是其唯一的标识符,所有的函数在编译后都会直接映射为唯一的符号名。换句话说,C语言的函数名没有额外的修饰信息来区分参数类型或数量。
示例:

void func(int a);
void func(double b); // 错误:重复定义函数名

在C语言中,这两个函数的名字都是func,会导致符号重定义错误,因为编译器无法通过参数类型来区分它们。与此相比,C++通过**名称修饰(Name Mangling)**机制对函数名进行编码,支持多个同名函数的共存。

2.C语言不具备函数签名的概念
C语言中的函数签名(函数的唯一标识符)只包含函数名,而不包括参数类型或数量。
例如,以下两个函数在C语言中有相同的签名:

void func(int a);
void func(double b);

C语言编译器仅关注函数名,因此认为这两个函数是重复定义的,导致编译错误。在C++中,函数签名包括函数名和参数列表,这使得C++能够区分同名函数。

3.C语言编译器不支持名称修饰
C++中,编译器通过名称修饰(Name Mangling)为每个函数生成独特的符号名。例如:

void func(int a);    // 编译后:_Z4funci
void func(double b); // 编译后:_Z4funcd

而C语言的编译器不会对函数名进行修饰。所有的函数符号直接与函数名绑定,因此不可能通过额外的信息(如参数类型)来区分同名函数。

4.C语言不支持类型检查扩展
C语言不支持函数签名中包含参数类型,因此无法通过类型检查来选择合适的函数版本。而C++引入了类型检查和重载解析机制,可以根据调用时提供的参数类型,选择最佳匹配的重载函数
在C++中,以下代码可以根据参数类型自动调用正确的函数:

void func(int a);
void func(double b);func(5);    // 调用func(int)
func(3.14); // 调用func(double)

而在C语言无法实现类似的机制,因为它没有参数类型的匹配逻辑。

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

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

相关文章

关于Postgresql旧版本安装

抛出问题 局点项目现场&#xff0c;要求对如下三类资产做安全加固&#xff0c;需要在公司侧搭建测试验证环境&#xff0c;故有此篇。 bclinux 8.2 tomcat-8.5.59 postgrel -11 随着PG迭代&#xff0c;老旧版本仅提供有限维护。如果想安装老版本可能就要费劲儿一些。现在&…

使用echarts实现3d柱状图+折线图

以下代码有问题请直接问国内直连GPT/Claude HTML 需要注意threeDchart一定要设置宽度高度&#xff0c;不然图不显示,然后echarts版本不要太低&#xff0c;不然也不显示 <div id"threeDchart" class"threeDchart"></div>js set3DBarChart2(dat…

2024.1212-02-虚拟私人网(VPN) 虚拟局域网 及隧道技术(四)--GRE47 Etherip97 原理及应用

虚拟局域网 及隧道技术&#xff08;四&#xff09;-GRE47 & Etherip97原理及应用 概述原理及应用EOIP/Etherip概念区别 隧道协议标准EtherIP &#xff08;IP protocol number 97&#xff09;GRE 开源工具katlogic-eoip 验证环境GRE&#xff08;EOIP&#xff09;演示验证Eth…

一个初始化bitmap的小算法

一个初始化bitmap小算法 根据长度&#xff0c;创建bitmap初始化bitmap 根据长度&#xff0c;创建bitmap 看到一个开源项目&#xff0c;利用bitmap存储数据&#xff0c;其中创建和初始化过程&#xff0c;比较经典。这里摘录出来&#xff0c;以备后续使用。代码采用的是golang …

【从零开始入门unity游戏开发之——C#篇01】理论开篇,理解什么是编程

文章目录 前言前置条件进制什么是十进制、二进制二进制有什么用&#xff1f;为什么计算机用二进制而不用十进制&#xff1f;二进制转十进制十进制转二进制二进制运算 计算机中的数据存储单位什么是编程&#xff1f;什么是代码&#xff1f;什么是编程语言&#xff1f;常见的编程…

黑盒白盒测试

任务1 黑盒测试之等价类划分法 【任务需求】 【问题】例&#xff1a;某报表处理系统要求用户输入处理报表的日期&#xff0c;日期限制在2003年1月至2008年12月&#xff0c;即系统只能对该段期间内的报表进行处理&#xff0c;如日期不在此范围内&#xff0c;则显示输入错误信息…

CSS学习记录11

CSS布局 - display属性 display属性是用于控制布局的最终要的CSS属性。display 属性规定是否/如何显示元素。每个HTML元素都有一个默认的display值&#xff0c;具体取决于它的元素类型。大多数元素的默认display值为block 或 inline。 块级元素&#xff08;block element&…

ByteCTF2024

wp参考&#xff1a; 2024 ByteCTF wp 2024 ByteCTF WP- Nepnep ByteCTF 2024 writeup by Arr3stY0u 五冠王&#xff01;ByteCTF 2024 初赛WriteUp By W&M ByteCTF 2024 By W&M - W&M Team ByteCTF Re WP - 吾爱破解 - 52pojie.cn 2024 ByteCTF - BediveRe_R…

C#,在 C# 语言中将 LaTeX 转换为 PNG 或 JPG 图像

在 C 语言中将 LaTeX 转换为 PNG 或 JPG 图像# 12月 28&#xff0c; 2021 2 分钟 法尔汉拉扎 在 C 语言中将 TeX 转换为 PNG JPG 图像# TeX 格式用于处理技术和科学文件。它通常用于交流或发布此类文档。在某些情况下&#xff0c;您可能需要将 TeX 文件渲染为 PNG 或 JPG 等图像…

AI监控赋能健身馆与游泳馆全方位守护,提升安全效率

一、AI视频监控技术的崛起 随着人工智能技术的不断发展&#xff0c;AI视频监控正成为各行业保障安全、提升效率的关键工具。相比传统监控系统&#xff0c;AI技术赋予监控系统实时分析、智能识别和精准预警的能力&#xff0c;让“被动监视”转变为“主动防控”。 二、AI监控应用…

搭建Tomcat(一)---SocketServerSocket

目录 引入1 引入2--socket 流程 Socket&#xff08;应用程序之间的通讯保障&#xff09; 网卡(计算机之间的通讯保障) 端口 端口号 实例 client端 解析 server端 解析 相关方法 问题1&#xff1a;ServerSocket和Socket有什么关系&#xff1f; ServerSocket Soc…

爬虫学习案例5

爬取b站一个视频 罗翔老师某一个视频很刑 单个完整代码&#xff1a; 安装依赖库 pip install lxml requests import osimport requests import re from lxml import etree import json # 格式化展开输出 from pprint import pprint # 导入进程模块 import subprocess head…

10.python函数

你提供的信息非常全面地介绍了Python函数的基础概念和用法。为了确保内容的现代性和准确性&#xff0c;我将对一些细节进行更新&#xff0c;并提供一些Python 3.x版本中的最佳实践。 文章目录 Python 函数定义一个函数实例&#xff1a;打印传入的字符串函数调用参数传递传不可…

【深度学习】 零基础介绍卷积神经网络(CNN)

零基础介绍 卷积神经网络&#xff08;CNN&#xff0c;Convolutional Neural Network&#xff09;是深度学习中的一种神经网络&#xff0c;特别擅长处理图像和视频等有空间结构的数据。 假设我们在做一个“照片分类”的任务&#xff0c;比如判断一张照片中是猫还是狗。下面用一…

【计算机组成原理】实验二:通用寄存器单元实验

实验二&#xff1a;通用寄存器单元实验 一、实验目的 了解通用寄存器的组成和硬件电路&#xff0c;利用通用寄存器实现数据的置数、左移、右移等功能。 二、实验内容 数据输入通用寄存器 寄存器内容无进位位左移实验 寄存器内容无进位位右移实验 三、实验步骤和结果 实…

boost之bind

简介 用于构造函数对象&#xff0c;其定义在文件bind.hpp中 bind 其底层使用通用的模板类bind_t template<class R, class F, class L> class bind_t { public:typedef bind_t this_type;bind_t(F f, L const & l): f_(f), l_(l) {}#define BOOST_BIND_RETURN re…

4G模块详解

在之前的教程中&#xff0c;无线通信技术我们学习了蓝牙和 WiFi&#xff0c;今天我们要来学习 4G。 4G 模块在距离上有个突破&#xff0c;它不像蓝牙短距离&#xff0c;也不像 WiFi 只能在局域网&#xff0c;4G 模块可使用户无论在哪&#xff0c;只要有 4G 网络信号覆盖&#…

QT数据库操作详解

在Qt中&#xff0c;操作数据库通常使用Qt SQL模块&#xff0c;该模块提供了一组类来与数据库进行交互。 数据库连接与查询执行 QSqlDatabase::addDatabase(): 添加一个数据库连接。 QSqlDatabase::open(): 打开数据库连接。 QSqlDatabase::close(): 关闭数据库连接。 QSql…

如何实现接口继承与实现继承的区别?如何处理多态性与性能的平衡?

在面向对象编程中&#xff0c;接口继承和实现继承是两个重要的概念&#xff0c;同时多态性也是面向对象编程的一大特性&#xff0c;但在追求多态性的同时&#xff0c;我们也需要考虑性能问题。本文将详细探讨接口继承与实现继承的区别&#xff0c;以及如何处理多态性与性能的平…

Visual Studio 使用 GitHub Copilot 聊天

&#x1f380;&#x1f380;&#x1f380;【AI辅助编程系列】&#x1f380;&#x1f380;&#x1f380; Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码Visual Studio 安装和管理 GitHub CopilotVisual Studio 使用 GitHub Copilot 扩展Visual Studio 使用 GitHu…