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

C++ inline 的更进一步理解

文章目录

      • 1.概述
      • 2.ODR(One Definition Rule)问题
      • 3.范例测试代码
      • 4.好坏分析

ODR: One Definition Rule,即单一定义规则, C++ 语言中非常重要的一项规则,它确保程序的行为一致性并避免链接时出现冲突。ODR 的核心思想是在整个程序中,每个实体(如变量、函数、类)应该有且仅有一个定义。

在一个程序的所有翻译单元(通常是源文件)中,同一个函数或变量只能有一个定义。如果某个函数或变量在多个翻译单元中有多个定义,那么编译器在链接阶段会报错。这是为了避免在程序中调用某个函数时,编译器不确定应该使用哪一个版本的定义。

inline: 在 C++ 中一般用于建议编译器将函数在调用处进行展开,从而减少函数调用的开销。它是一种编译器优化机制,通常用于小型、简单的函数。


1.概述

现代 C++ 中,inline 的一个重要作用是处理 One Definition Rule (ODR) 问题。

ODR 规定在一个程序中,每个非内联的实体(比如函数或变量)只能有一个定义。然而,如果我们在头文件中定义一个独立函数(而不是声明),并在多个源文件中包含这个头文件,那么编译过程中会产生多个相同的独立函数定义,从而在链接时引发错误。


2.ODR(One Definition Rule)问题

#ifndef _______
#define _______
#include <iostream>void print_message(){std::cout << "Hello,World" << std::endl;
}#endif

多个源码文件包含时:

chVYgFn.o:(.bss+0x0): first defined here
/usr/bin/ld: /tmp/ccMy1ZtX.o: in function `print_message()':
call.cpp:(.text+0x0): multiple definition of `print_message()'; /tmp/cchVYgFn.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

解决这个问题,C++11及之前,可以改为 static 函数

C++17及之后,可以如下修改:

#ifndef _______
#define _______
#include <iostream>inline void print_message(){std::cout << "Hello,World" << std::endl;
}#endif

这里 inline 的作用是告诉编译器和链接器:

  • 这个独立函数或者变量可以在多个翻译单元中定义。
  • 所有这些定义都应该被视为等效的同一个实体(允许多个TU中存在相同定义而不违背ODR,链接时按编译器实现链接某个实现)。

3.范例测试代码

header.h

#ifndef _______
#define _______
#include <iostream>// inline :C++ 17 可以使用 inline,允许多个TU中存在相同定义而不违背ODR,链接时按编译器实现链接某个实现
// static : 也可以使用 static// static/inline
int global_count = 0;// ODR ( One Definition Rule )
// inline 不光是展开,当头文件在多个头文件包含时,
//     没有inline就会存在多个相同函数,造成重定义。
//     但是添加inline,就会保持一份定义。并且与包含一份的 #ifndef / #define 无关。
// inline :C++ 17 可以使用 inline,允许多个TU中存在相同定义而不违背ODR,链接时按编译器实现链接某个实现
// static : 也可以使用 static// static/inline
void print_message(){std::cout << "Hello,World" << std::endl;
}#endif

call.cpp

#include "header.h"

main.cpp

#include "header.h"
#include "header.h"int main(){}

编译:

[root@VM-24-13-centos inline_ODR]# g++ main.cpp call.cpp
/usr/bin/ld: /tmp/ccMo6TBR.o:(.bss+0x0): multiple definition of `global_count'; /tmp/ccEKrehI.o:(.bss+0x0): first defined here
/usr/bin/ld: /tmp/ccMo6TBR.o: in function `print_message()':
call.cpp:(.text+0x0): multiple definition of `print_message()'; /tmp/ccEKrehI.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
[root@VM-24-13-centos inline_ODR]#

声明为 staticinlinestatic inline 便不再报错

#ifndef _______
#define _______
#include <iostream>static inline
int global_count = 0;static inline
void print_message(){std::cout << "Hello,World" << std::endl;
}#endif

现代C++更趋向于头文件包含实现,这样做就可以避免C++的ODR问题。


4.好坏分析

static

  • static 关键字用于将函数的链接类型设为内部链接。这意味着该函数的作用域仅限于定义它的翻译单元(通常是一个源文件)。
  • 使用 static 修饰的函数在整个程序中是本地的,它不会在其他翻译单元中可见。因此,即使在不同的源文件中定义了同名的 static 函数,它们是互相独立的,不会引起冲突。
  • static 本身并不会提示编译器进行函数展开。它只影响作用域和链接。
  • 如果要 static 函数被展开,可以配合 inline 使用(static inline),这样既保证本地性又增加展开的可能性。

inline

  • inline 函数的链接类型为外部链接(如果没有其他修饰符影响)。它可以在多个翻译单元中使用,但这些翻译单元中的定义必须一致。
  • 编译器会将所有使用 inline 关键字定义的同名函数视为同一个函数,并在链接阶段合并这些定义,从而避免 ODR(One Definition Rule) 问题。
  • inline 是对编译器的建议,提示它可以在调用处直接展开函数体,从而减少函数调用的开销。但这不是强制的,最终是否展开由编译器决定。
  • 在现代编译器中,即使没有 inline,编译器也能根据优化设置自动决定是否展开,因此 inline 更多是解决链接和 ODR 问题的工具。

对比

static:本地可见,多份存在,互不冲突。
inline:被引用时,多份编译单元允许相同存在,解决 ODR 问题,并提示编译器可展开函数。

即,期望应用如下

  • 头文件中定义一个函数并希望它能被多个翻译单元使用,又不想发生链接冲突,就用 inline (C++17起)
  • 期望一个函数只在当前翻译单元内可见并且避免与其他文件的同名函数冲突,就用 static

相关文章:

C++ inline 的更进一步理解

文章目录 1.概述2.ODR(One Definition Rule)问题3.范例测试代码4.好坏分析 ODR: One Definition Rule&#xff0c;即单一定义规则&#xff0c; C 语言中非常重要的一项规则&#xff0c;它确保程序的行为一致性并避免链接时出现冲突。ODR 的核心思想是在整个程序中&#xff0c;每…...

海康威视云台相机图像获取

直接上代码&#xff1a; import cv2# 替换为正确的RTSP链接 rtsp_url rtsp://admin:abcd12345192.168.1.64:554/h264/ch1/main/av_stream cap cv2.VideoCapture(rtsp_url)if not cap.isOpened():print("无法打开视频流&#xff0c;检查RTSP URL和凭证") else:whil…...

什么是词嵌入(Word Embedding)

1. 什么是词嵌入(Word Embedding) ⾃然语⾔是⼀套⽤来表达含义的复杂系统。在这套系统中&#xff0c;词是表义的基本单元。顾名思义&#xff0c;词向量是⽤来表⽰词的向量&#xff0c;也可被认为是词的特征向量或表征。把词映射为实数域向量的技术也叫词嵌⼊&#xff08;word e…...

LSTM时间序列模型实战——预测上证指数走势

LSTM时间序列模型实战——预测上证指数走势 关于作者 作者&#xff1a;小白熊 作者简介&#xff1a;精通python、matlab、c#语言&#xff0c;擅长机器学习&#xff0c;深度学习&#xff0c;机器视觉&#xff0c;目标检测&#xff0c;图像分类&#xff0c;姿态识别&#xff0c;…...

基于 STM32F407 的 SPI Flash下载算法

目录 一、概述二、自制 FLM 文件1、修改使用的芯片2、修改输出算法的名称3、其它设置4、修改配置文件 FlashDev.c5、文件 FlashPrg.c 的实现 三、验证算法 一、概述 本文将介绍如何使用 MDK 创建 STM32F407 的 SPI Flash 下载算法。 其中&#xff0c;SPI Flash 芯片使用的是 W…...

力扣之1355.活动参与者

题目&#xff1a; Sql 测试用例&#xff1a; Create table If Not Exists Friends (id int, name varchar(30), activity varchar(30)); Create table If Not Exists Activities (id int, name varchar(30)); Truncate table Friends; insert into Friends (id, name, acti…...

数据资产治理:构建敏捷与安全的数据管理体系

在当今数字化的盛况下&#xff0c;作为核心资产的数据已经越发受到企业的重视。但是随着公司的逐步壮大&#xff0c;如何分析这些数据以及如何有效治理数据资产&#xff0c;以确保安全性、合规性以及易用性&#xff0c;是企业面临的重大挑战。数聚股份将从多年从业经验深度探讨…...

Nodejs连接Mysql笔记

框架搭建 安装Node.js 首先&#xff0c;确保你已经在系统上安装了Node.js和npm&#xff08;Node Packaged Modules&#xff09;。你可以通过以下命令检查是否已经安装&#xff1a;shell 或者 node -v 或者 npm -v 数据库连接代码 1.导入MySQL2库 npm install mysql2 2.在文件…...

Canvas:AI协作的新维度

在人工智能的浪潮中&#xff0c;OpenAI的最新力作Canvas&#xff0c;不仅是一款新工具&#xff0c;它标志着人工智能协作方式的一次革命性飞跃。Canvas为写作和编程提供了一个全新的交互界面&#xff0c;让用户能够与ChatGPT进行更紧密、更直观的协作。 ​​​​​​​ Canvas的…...

【深度学习】— softmax回归、网络架构、softmax 运算、小批量样本的向量化、交叉熵

【深度学习】— softmax回归、网络架构、softmax 运算、小批量样本的向量化、交叉熵 3.4 Softmax 回归3.4.1 分类问题3.4.2 网络架构 3.4.3 全连接层的参数开销3.4.4 softmax 运算3.4.5 小批量样本的向量化3.4.6 损失函数对数似然softmax 的导数 3.4.7 信息论基础熵信息量重新审…...

C# Wpf 图片按照鼠标中心缩放和平移

C# Wpf 图片按照鼠标中心缩放和平移 1、缩放事件 MouseWheel(object sender, MouseWheelEventArgs e)2、平移相关的事件 MouseMove(object sender, MouseEventArgs e) MouseDown(object sender, MouseButtonEventArgs e) MouseUp(object sender, MouseButtonEventArgs e)3、…...

网络安全产品类型

1. 防火墙&#xff08;Firewall&#xff09; 功能&#xff1a;防火墙是网络安全的第一道防线&#xff0c;通过检查进出网络的流量来阻止未经授权的访问。它可以基于预定义的安全规则&#xff0c;过滤数据包和阻止恶意通信。 类型&#xff1a; 硬件防火墙&#xff1a;以专用设备…...

【开源风云】从若依系列脚手架汲取编程之道(五)

&#x1f4d5;开源风云系列 &#x1f34a;本系列将从开源名将若依出发&#xff0c;探究优质开源项目脚手架汲取编程之道。 &#x1f349;从不分离版本开写到前后端分离版&#xff0c;再到微服务版本&#xff0c;乃至其中好玩的一系列增强Plus操作。 &#x1f348;希望你具备如下…...

金融市场的衍生品交易及其风险管理探讨

金融衍生品市场是现代金融体系的重要组成部分&#xff0c;其交易量和复杂性在过去几十年中迅速增长。衍生品&#xff0c;如期权、期货、掉期等&#xff0c;因其灵活性和杠杆效应&#xff0c;广泛应用于风险管理、投机和资产配置等多个领域。本文将探讨金融衍生品交易的关键特点…...

一、创建型(单例模式)

单例模式 概念 单例模式是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。它控制类的实例化过程&#xff0c;防止外部代码创建新的实例。 应用场景 日志记录&#xff1a;确保只有一个日志记录器&#xff0c;以便于管理和避免重复记…...

毕业设计项目-古典舞在线交流平台的设计与实现(源码/论文)

项目简介 基于springboot实现的&#xff0c;主要功能如下&#xff1a; 技术栈 后端框框&#xff1a;springboot/mybatis 前端框架&#xff1a;html/JavaScript/Css/vue/elementui 运行环境&#xff1a;JDK1.8/MySQL5.7/idea&#xff08;可选&#xff09;/Maven3&#xff08…...

【秋招笔试】10.09华子秋招(已改编)-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 本次的三题全部上线…...

【算法笔记】双指针算法深度剖析

【算法笔记】双指针算法深度剖析 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;算法笔记 文章目录 【算法笔记】双指针算法深度剖析前言一.移动零1.1题目1.2思路分析1.3代码实现 二.复写零2.1题目2.2思路分析2.3代码实现 三.快乐数3.1题目…...

第二十二天|回溯算法| 理论基础,77. 组合(剪枝),216. 组合总和III,17. 电话号码的字母组合

目录 回溯算法理论基础 1.题目分类 2.理论基础 3.回溯法模板 补充一个JAVA基础知识 什么时候用ArrayList什么时候用LinkedList 77. 组合 未剪枝优化 剪枝优化 216. 组合总和III 17. 电话号码的字母组合 回溯法的一个重点理解&#xff1a;细细理解这句话&#xff01;…...

关闭IDM自动更新

关闭IDM自动更新 1 打开注册表2 找到IDM注册表路径 1 打开注册表 winR regedit 2 找到IDM注册表路径 计算机\HKEY_CURRENT_USER\Software\DownloadManager 双击LstCheck&#xff0c;把数值数据改为0 完成 感谢阅读...

Go 性能剖析工具 pprof 与 Graphviz 教程

在 Golang 开发中&#xff0c;性能分析是确保应用高效运行的重要环节。本文介绍如何使用 gin-contrib/pprof 在 Gin 应用中集成性能剖析工具&#xff0c;并结合 Graphviz 生成图形化的性能分析结果&#xff0c;如火焰图。这套流程帮助开发者更好地理解和优化 Go 应用的性能。 目…...

【题目解析】蓝桥杯23国赛C++中高级组 - 斗鱼养殖场

【题目解析】蓝桥杯23国赛C中高级组 - 斗鱼养殖场 题目链接跳转&#xff1a;点击跳转 前置知识&#xff1a; 了解过基本的动态规划。熟练掌握二进制的位运算。 题解思路 这是一道典型的状压动态规划问题。设 d p i , j dp_{i, j} dpi,j​ 表示遍历到第 i i i 行的时候&a…...

JavaScript可视化:探索顶尖的图表库

JavaScript可视化&#xff1a;探索顶尖的图表库 在这个被数据驱动的时代&#xff0c;你有没有想过&#xff0c;数据本身是如何变得有意义的&#xff1f;答案就是数据可视化。通过图表和图形&#xff0c;我们不仅可以看到数据&#xff0c;还可以感受到它&#xff0c;从而做出明…...

谷歌AI大模型Gemini API快速入门及LangChain调用视频教程

1. 谷歌Gemini API KEY获取及AI Studio使用 要使用谷歌Gemini API&#xff0c;首先需要获取API密钥。以下是获取API密钥的步骤&#xff1a; 访问Google AI Studio&#xff1a; 打开浏览器&#xff0c;访问Google AI Studio。使用Google账号登录&#xff0c;若没有账号&#xf…...

进入容器:掌控Docker的世界

进入容器:掌控Docker的世界 在这个快速发展的技术时代,你是否曾被Docker的庞大生态所吸引?那么,有没有想过在这个容器化的世界里,如何快速高效地“进入”这些隐藏在虚拟墙后的容器呢?容器就如同魔法箱,装载着应用与服务,而你,通过探索这些容器,能够更好地管理、排除…...

初始Linux(二)基础命令

前言&#xff1a; 之前那一篇我们已经介绍了一部分的基础命令&#xff0c;当然那只不过是九牛一毛&#xff0c;本篇我们继续介绍一些比较重要且需要掌握的基础命令。 mv命令&#xff1a; 其实这个命令有两个功能&#xff0c;一个是移动&#xff08;剪切&#xff09;文件&#…...

STM32 OLED

文章目录 前言一、OLED是什么&#xff1f;二、使用步骤1.复制 OLED.C .H文件1.1 遇到问题 2.统一风格3.主函数引用头文件3.1 oled.h 提供了什么函数 4.介绍显示一个字符的函数5. 显示十进制函数的讲解 三、使用注意事项3.1 配置符合自己的引脚3.2 花屏总结 前言 提示&#xff…...

伦敦金实时行情决策辅助!

在伦敦金实时交易的过程中&#xff0c;投资者主要依赖技术分析来辅助自己的投资决策。与基本面分析不同&#xff0c;技术分析侧重于研究金价的走势和市场行为&#xff0c;通过图表和技术指标来预测未来的市场走势。常用的技术分析方法包括&#xff1a; 趋势线和支撑阻力位&…...

​Leetcode 746. 使用最小花费爬楼梯​ 入门dp C++实现

问题&#xff1a;Leetcode 746. 使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你…...

路由协议常见知识点

路由协议是网络通信的基础&#xff0c;主要负责在网络中传递数据包&#xff0c;并确保它们从源节点传递到目标节点。本文将介绍一些常见的路由协议知识点&#xff0c;包括路由协议的分类、特性、配置与管理以及常见问题。 一、路由协议的分类 距离矢量路由协议&#xff1a; R…...