c++ - 对内联行为 C++ 的困惑
问题描述
我正在学习 C++,但我对内联行为感到困惑。在cppreference上,我发现“多个源文件中包含的函数必须是内联的”。他们的例子如下:
// header file
#ifndef EXAMPLE_H
#define EXAMPLE_H
// function included in multiple source files must be inline
inline int sum(int a, int b)
{
return a + b;
}
#endif
// source file #2
#include "example.h"
int a()
{
return sum(1, 2);
}
// source file #1
#include "example.h"
int b()
{
return sum(3, 4);
}
这让我有点困惑——我认为 ifndef 守卫正是在做这项工作,即防止多次包含同一个文件时出现问题。无论如何,我想测试一下,所以我准备了以下内容:
// Sum.h
#ifndef SUM_H
#define SUM_H
int sum(int a, int b);
#endif
// Sum.cpp
int sum(int a, int b){
return a + b;
}
// a.h
#ifndef A_H
#define A_H
int af();
#endif
// a.cpp
#include "sum.h"
int af(){
return sum(3, 4);
}
// b.h
#ifndef B_H
#define B_H
int bf();
#endif
// b.cpp
#include "sum.h"
int bf(){
return sum(1, 2);
}
// main.cpp
#include "sum.h"
#include "a.h"
#include "b.h"
#include <iostream>
int main() {
std::cout << af() + bf();
}
这按预期工作正常。然后我在 sum.cpp 和 sum.h 中使用将 sum 函数定义为内联,编译失败:
"sum(int, int)", referenced from:
bf() in b.cpp.o
af() in a.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
有人可以为我澄清一下吗?
解决方案
包含防护 with#ifndef/#define/#endif
或 with#pragma once
仅防止包含每个翻译单元。
让我们假设你有一个像这样的标题:
#pragma once
void Do() {}
和两个*.cpp
包含在内的文件。如果你现在编译
g++ source1.cpp source2.cpp
你会得到一个
xy 行/文件中 Do() 的多重定义
每个源文件都将“单独”编译,因此从第一个翻译单元设置的第二个翻译单元看不到保护。两种翻译(编译)都是完全独立的。因此,包含防护在这种情况下不会保护任何东西。
为此,可以将函数定义定义为inline
。现在这两个定义都将呈现给链接器,但标记为“弱”。链接器现在没有抱怨这两个定义,只接受其中一个(通常是最后一个!),这并不重要,因为在这种情况下两者都是相同的。
所以包含保护的意义在于,您可以将一个文件多次包含到一个翻译单元中。这通常仅在您间接包含标头时才会发生。假设 ah 有函数定义并且 bh 和 ch 都包含 ah 如果你的 cpp 文件现在包含 bh 和 ch 它们都包含 ah 所以你有多个定义,没有包含保护。这是包含守卫的用例。
如果函数已定义inline
但在所有使用翻译单元中不可见,则相反的问题会出现“未定义符号”:
例子:
拥有该文件:
inline void sum() {}
void f1() { sum(); }
并编译-O0
产生,输出nm f1.o|c++filt
0000000000000000 T f1()
0000000000000000 W sum()
我们看到定义为弱的符号sum
,因此它可以在链接阶段多次出现。如果没有“看到”定义的第二个翻译单元将毫无问题地链接,也将使用它。
但是使用“-O3”你会得到:
0000000000000000 T f1()
那是特定于编译器的实现!编译器可以提供内联函数。如果使用更高的优化级别,它们通常不会。
作为一个规则:如果一个函数被定义inline
,它的定义必须在它被使用之前对每个翻译单元都是可见的!
推荐阅读
- three.js - 地板上的阴影在 three.js v104 中不起作用,但在 r71 中起作用
- algorithm - +1 在硬币找零问题的递归关系中意味着什么(动态规划方法)?
- node.js - 使用 Node 中的单个流将多个输入文件传递给 ffmpeg
- docker - 使用端口暴露映射连接本地主机中的两个 docker 实例
- xslt - BizTalk 映射抑制目标中的空属性
- php - 无法将 phpredis 与 Docker 一起使用。找不到 Redis 类
- netsuite - 如何延长netsuite中第三方URL的响应时间?即,避免大型请求的 SSS_REQUEST_TIME_EXCEEDED
- android - 有没有办法为日历视图中选择的每个日期保存不同的活动实例?
- azure-language-understanding - 删除的话语是否会影响 LUIS 话语配额?
- reactjs - 有没有一种简单的方法来使用 Apollo Client 2.5 for React 管理本地状态?与“client.writeData”等效的“读取”是什么?