01|Modules(上):C++模块化问题的前世今生
万物始于 include
- 深入了解
- 翻译
- 解释
- 总结
C++20中的核心特性变更——Modules模块,为现代C++的编程范式、编程思想及现实问题解决方法带来革新。文章首先介绍了C/C++编程中使用include头文件机制的基本方式,但指出了其存在的难以解决的两难问题。随着软件技术的发展,C++从C++98过渡到现代C++之后,越来越多的特性可以被定义或声明在头文件中,导致一系列问题的出现,如模糊的模块划分、依赖符号导入顺序、编译效率低下和命名空间污染。 在传统C++编程中,虽然在C++20之前没有统一的抽象模块概念,但开发者总结出了一套较为行之有效的经验,以达到划分业务逻辑代码和提升代码复用性的目的。传统项目中,常用特性包括编译链接、头文件和命名空间来模拟模块。 总的来说,文章通过介绍C++头文件机制存在的问题以及传统C++模块化解决方案,引出了C++ Modules的重要性和必要性,为读者提供了对C++模块化问题的深入了解和思考。
《现代 C++20 实战高手课》,新⼈⾸单¥59
全部留言(9)
- 最新
- 精选
- 糍粑不是饭老师是否考虑再增加些CMake或者包管理相关的课程呢?😃
作者回复: Good point! 正好在这里做个调研,大家是否对这个话题感兴趣呢?
2023-03-18归属地:北京46 - tanatang写面向对象的C++,成员和函数都在设计在类中,禁止使用这种不属于任何类的全局函数, 全局变量。
作者回复: OOP原教旨主义 👍
2023-01-18归属地:四川5 - wilby为什么namespace让符号管理变得很复杂?老师好像就给了个结论,能展开讲讲么?
作者回复: 文中的意思并非说namespace导致符号管理变复杂。这里的意思是,诸如namespace这类语言特性可以解决符号冲突问题,并且可以在编译时给予友好的提示,但由于C++的编译链接分离机制,C++只为链接定义了一些指导性原则,链接中很多具体技术规范大多由ABI来定义,这就导致在不同编译器和不同体系结构下可能在链接时会遇到不同的问题,这才是C++符号管理复杂性的核心问题所在。而C++ Modules则是更完备的符号可见性管理语言特性,可以与namespace实现互补,而且在链接上也给出了更多的原则(虽然还是无法完全解决ABI问题),让符号管理更加易于理解,更接近现代编程语言,可以一定程度降低整体符号管理的复杂性。
2023-01-18归属地:瑞典25 - Geek_7c0961"这也能大量减少编译单元之间的符号冲突问题,毕竟可能出现,两个编译单元定义了同名,但只想在编译单元内部使用函数的情况,我们并不想给这些函数加上冗长的前缀。那这个时候,只需要使用 static 修饰符。比如我们可以在 A 和 B 中都定义 static 函数 to_int,然后再编译链接,这样就不会出现符号冲突的问题。" 这块儿能否给个具体的代码示例?
作者回复: 比如以下代码 a.cpp: #include <iostream> static void print() { std::cout << "Print in A" << std::endl; } void fa() { print(); } b.cpp: #include <iostream> static void print() { std::cout << "Print in B" << std::endl; } void fb() { print(); } main.cpp extern void fa(); extern void fa(); int main() { fa(); fb(); return 0; } 这里a.cpp和b.cpp两个编译单元都有print函数,但是函数使用了static修饰符,因此print函数仅对各自编译单元内部可见,所以链接时不会导致符号冲突问题。会正确输出: Print in A Print in B
2023-01-18归属地:美国32 - 小样太可怕了,以前没有想过这种问题。 实际用到的代码规模并不是很大,一个人就能完全掌握各个模块,也就不会有冲突。现在来看传统上符号隔离和污染问题根本就没有在设计上解决过。软件工程规模大后必然有冲突问题。
作者回复: 是的,所以软件工程需要确定种种规范,就是为了尽量规避这些问题,但在实际的大项目中还是很难完全避免,尤其是要集成大量第三方库的时候会更加头痛。 经验积累很重要,而且很值钱。记得总结出自己的一套实践方法。
2023-01-24归属地:江西1 - tang_ming_wu链接过程,函数地址填充,是不是都是相对地址?
作者回复: 不一定,C++并没有定义二进制函数地址填充的方式,这完全取决于操作系统与特定体系结构的ABI,有可能是相对地址,也有可能是绝对地址。
2023-01-17归属地:广东1 - peter请教两个问题: Q1:第一个专栏是什么? Q2:实现放在头文件中为什么可以提高运行性能? 文中有这样一句话“但是为了提高运行时性能,开发者也会考虑将实现直接放在头文件中。” 为什么?
作者回复: A1:《动态规划面试宝典》 A2:因为C++编译期支持针对函数调用的内联优化。 将函数实现放到头文件中,编译期可以根据实际情况对函数调用进行优化,比如将函数调用替换成函数的实现代码,这样针对部分较短的函数可以抵消掉函数调用的性能损耗,这也就是为何明确标为inline的函数定义都一定要放在头文件中。
2023-01-17归属地:北京21 - 中山浪子“ b.o 中会生成一个名为 add 的符号”一般是用什么工具或者怎么查看生成的二进制中的符号?
作者回复: 可以用binutils中的objdump/nm查看符号,如果是Windows下生成的PE/COFF格式的二进制文件,还可以用VC的dumpbin查看符号
2024-01-03归属地:江苏 - 不二历史挺好的,但是里面概念太多,解释不清楚感觉消化不了。
作者回复: 掌握C++的过去,才能更好的理解C++的未来。虽然这是C++编程语言复杂性的一个缩影,但是有助于大家理解现代C++。如果对这些旧有概念感兴趣,可以回顾一下这些概念。有任何问题也欢迎在评论区提出,我会跟你一起探讨。
2023-01-19归属地:浙江