首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

C++防止头文件被重复引入3种方法!

之前我们详细介绍了 C 语言中如何使用宏定义(#ifndef / #define / #endif)来有效避免头文件被重复 #include,方式 C++ 多文件编程也很常用。...这是因为 school.h 文件已经 #include 了一次 "student.h",而在 main.cpp 主程序又同时 #include 了 "school.h" 和 "student.h",...当程序第一次 #include 该文件时,由于 _NAME_H 尚未定义,所以定义 _NAME_H 并执行“头文件内容”部分代码;当发生多次 #include 时,因为前面已经定义了 _NAME_H...但值得一提是,并不是每个版本编译器都能识别 #pragma once 指令,一些较老版本编译器就不支持该指令(执行时会发出警告,但编译继续进行),即 #pragma once 指令兼容性不是很好...事实上,无论是 C 语言还是 C++,为防止用户重复引入系统库文件,几乎所有库文件中都采用了以上 3 种结构一种,这也是为什么重复引入系统库文件编译器也不会报错原因。

4.7K40

C++奇迹之旅相遇:支持函数重载原理

再看图,我们来分析这个为什么是链接错误,可知道当Test.cpp,Stack.cpp,Stack.h这三个文件运行起来是,先进行预处理,预处理****就是把相应头文件展开,然后宏替换,然后条件编译等等...只有函数声明,把Stack.cpp定义去掉,可以过,因为语法检查是匹配,Test.cpp->Test.o过程没有函数地址,链接时,就要用StacklInit这个名字去Stack.o找他地址...C++ C++如此例子运行 这就回到了我们最初这个概念:这些同名函数形参列表(参数个数 或 类型类型顺序)不同,常用来处理实现功能类似数据类型不同问题 注意:以上情况是分多个文件才会发生这样情况...采用C语言编译器编译后结果 结论:linux下,采用gcc编译完成后,函数名字修饰没有发生改变。...采用C++编译器编译后结果 结论:linux下,采用g++编译完成后,函数名字修饰发生改变,编译器将函数参数类型信息添加到修改后名字

11410
您找到你想要的搜索结果了吗?
是的
没有找到

【C++】你想要——印刷模板儿

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数模具。所以其实模 板就是将本来应该我们做重复事情交给了编译器。  ...编译器通过类型推演,将函数模板进行实例化,对应T就会替换成具体类型,模板实例化是用几个实例化几个,不是所有不同类型都提前模板实例化。...注意:模板编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅 Add(a1, d1); 此时有两种处理方式:1. 用户自己来强制转化 2....他与普通数组最大区别是: 1. 普通数组对于数组越界这种情况,只能随机抽查!而我们自己实现类模板可以严格控制越界访问这种情况!别说越界修改,越界访问都不行!...但在链接时候,test.cpp,却不能找到它地址,这是为什么??这就是模板和其他区别! 链接错误原因: .cpp定义,不是实例化模板,他只是一个模板,没有任何实例化成任何类型

38430

详细剖析 extern C

这种嵌套是被C++规范允许。当嵌套发生时,以最内层嵌套为准。比如在下面代码,函数foo会使用C++链接规范,而函数bar则会使用C链接规范。...A: 链接规范仅仅用于修饰函数和变量,以及函数类型。所以,严格讲,你只应该把这三种对象放置于extern "C"内部。...某些人可能建议你,如果a.h没有extern "C",而b.cpp包含了a.h,可以b.cpp里加上 : extern "C" {   #include "a.h" } 这是一个邪恶方案,原因在之前我们已经阐述...虽然你拥有修改代码权限,但由于这个头文件属于遗留系统,冒然修改可能带来不可预知问题。 对 于第一种情况,不要试图自己进行workaround,因为这会给你带来不必要麻烦。...因为,首先,对于大多数头文件而言,这种修改都不是一种复杂,高风险修改,一切都在可控范围之 内;其次,如果某个头文件混乱而复杂,虽然对于遗留系统哲学应该是:“它还没有带来麻烦之前不要动它”,但现在麻烦已经来了

1.2K30

C++复习笔记——0_零碎问题及解决笔记

真正开发过程, 尽量避免使用 using namespace std;等直接引入整个命名空间,否则会因为命名空间污染导致很多不必要问题, 比如自己写某个函数,名称正好和 std 一样, 编译器不知道使用哪一个... C++ main 函数前面为什么要加上数据类型,比如: int void ? main 函数返回值是返回给主调进程,使主调进程得知被调用程序运行结果。...一些检查不是严格编译器,比如 VC, VS 等,void 类型 main 是允许。不过一些检查严格编译器下,比如 g++, 则要求 main 函数返回值必须为int 型。... C 语言中main()省略返回类型也就相当说明返回类型为 int 型,不过这种用法 C++ 逐渐被淘汰。...虽然 void main()很多系统都适用,但他毕竟不是标准,所以应该避免这种用法, 应该使用这种int main(void) 写法比较妥当。 11.

40130

Reddit 观察:你何时会考虑使用 Cpp 而非 Rust ?

最近他一个使用 Javascript 朋友问他:“你为什么要用 Cpp,它很糟糕,Rust 解决了 Cpp 所有问题”。 这是Rust社区一直使用主要口号之一。...我认为,在学习基本现代 Cpp 所需时间里,我可以掌握 Rust 以及其他几种编程语言。每当我了解 Cpp 新知识时,我都会想:“为什么 Cpp 做这个事情要比 Rust 复杂得多呢?...phazer99 : 这是百分之百正确。当你团队工作时,你真正欣赏到Rust 及其工具相对于 Cpp(以及其他语言)所提供优势。...harmic: 另一个关键因素:许多情况下,C++编译器错误信息非常糟糕。想象一下,从你错误使用某个模板库深处涌出一大堆无意义内容。...Cpp 频道评论 msqrt: 我曾经简单尝试过 Rust。确实,知道编译器可以很多错误发生之前就捕捉到它们,这让人感到非常放心。

23910

Unity手游实战:从0开始SLG——ECS战斗(六)Unity面向数据技术栈(DOTS)

操作系统统一管控计算机各个硬件资源,然后按照调度需求分别给不同进程执行指定时间片段,因为计算机处理速度非常快,所以让用户感觉同时运行多个程序(进程)。...但是这种模式也不是没有成本,当并行进程数量过多时候,切换进程代价就会非常大,因为它必须要先把当前上下文存储,然后加载新上下文,然后执行片段时间,备份存储,再执行下一个进程片段。...jobs System多线程 严格来说,Jobs System并不属于多线程编程范畴,因为它不能直接对线程进程操作。...IL2CPP IL2CPP看名字就看出来,这是一个将IL语言转换为CPP语言工具,看下它执行方式: ?...注意,我刚才其实有说IL2CPP抛弃了虚拟机,但是在上面的执行过程图里仍然有I2CPP VM过程,这是因为C#本身是基于托管代码设计语言,IL本身也是托管代码执行,所以IL2CPP即使将IL转为了

2.2K10

Carbon vs Rust | 你想要了解

我同样也对 Carbon 好奇,但是我不是好奇它语言语法设计,我是好奇它为什么会出现。 世界观下,任何一件人造新生事物出现,它一定是有原因。...创造 Carbon 动机是什么? “有人说这是 KPI 项目,也有人说这是 Cpp 标准委员内斗(阴谋论)结果,我觉得都不尽然。...因为 Rust 比 Cpp 编译器更加严格 Cpp 某些合理设计, Rust也许行不通。比如,C++ API 和数据结构设计时并未考虑 Rust 借用检查规则。...两者关键区别在于,模板参数只能在实例化过程完成类型检查,而可检查泛型则指定了一个接口,参数可以没有实例化情况下完成类型检查。...后者好处是: 泛型函数类型检查错误更早发生,使编译器更容易产生有用诊断。 泛型函数可以产生较少编译输出,使有许多用途编译变得更快。

1.3K10

【C++】模板进阶

,而不是left和right本身 } 有的同学可能会说,我直接重载一个参数类型为 Date* 函数即可,为什么要费这么大劲搞成模板特化呢?...函数声明和定义分离不是即能够方便别人阅读我们代码,还能够保护源码不被泄露吗?-- 这是因为模板不支持分离编译。...但是当我们编译运行时候我们发现分离定义所有成员函数都出现了链接性错误: 造成这种错误原因如下: C语言 程序环境和预处理 那一节我们学习了一个 .c/.cpp 程序要变成 .exe 可执行程序一共要经历四个步骤...需要将 Test.cpp 和 Stack.cpp 符号表内容进行合并与重定位,但是由于它们符号表都是无效地址,所以发生链接错误。...(这种方式使用于类较大时,方便别人快速了解我们类) 3、注:这两种方法都有一个缺点 – 暴露源码,因为函数声明和定义是一个文件,我们将类提供给别人使用时不得不将源码也暴露给别人,这也是模板一个缺点

40000

C语言中.h和.c文件解析(很精彩)

main在运行时就会找到这个定义了这个函数aaa.c文件。   这是因为: main函数为标准C/C++程序入口,编译器先找到该函数所在文件。   ...为什么经常见 xx.c 里面 include 对应 xx.h? 如果 a.c 不写,那么编译器不是自动把 .h 文件里面的东西跟同名 .c 文件绑定在一起?...用户只需要按照头文件接口声明来调用库功能,而不必关心接口怎么实现编译器从库中提取相应代码。   (2)头文件能加强类型安全检查。...这也正说明了,为什么很多编译器并不care到底这个文件后缀名是什么----因为#include预处理就是完成了一个"复制并插入代码"工作。...(非常重要)   VC,一帮情况下不需要自己写makefile,只需要将需要文件都包括project,VC自动帮你把makefile写好。

1.5K20

h文件和c文件区别include本身只是一个简单文件包含预处理命令,即为把include后面文件放到这条命令这里,除此之外,没有其它用处(至少我也样认为).

.c就是C语言系列源文件,以文本形式存在,而.h系列则是头文件,即C系列存放函数和全局变量文件,因为C函数是被封装起来,即无法看到其代码....1.为什么经常见 xx.c 里面 include 对应 xx.h? 2.如果 a.c 不写,那么编译器不是自动把 .h 文件里面的东西跟同名 .c 文件绑定在一起?...3.第三个问题我给他改了一下:如果 a.c 不写include,那么编译器不是自动把 .h 文件里面的东西跟同名.c文件绑定在一起?...但是如果.c函数也需要调用同个.c其它函数,那么这个.c往往include同名.h,这样就不需要为声明和调用顺序而发愁了(C语言要求使用之前必须声明,而include同名.h一般放在.c开头...,这是前提.如果你改了它扩展名那么你编译器还能认识它吗上升到一个更高层次上看待这个问题,XX兄说也不错我想XX兄说意思就是两者不可因为名字相同就认为两者有什么关系,名字是可以随便~ 两者之间联系

1.3K20

重点!_头文件&源文件&编译&链接

虽然,语法上,同一个数据类型(如一个class)不同源文件书写多次是允许,程序员认为他们是同一个自定义类型,但是,由于数据类型不具有外部连接特性,编译器并不关心该类型多个版本之间是否一致,这样有可能导致逻辑错误发生...a.cpp对于类a成员函数进行了定义,但a.cpp并没有类a声明 而a.h和a.cpp并没有关联,也就是说编译器不知道a.cpp类a在哪儿声明,而类a如果没有声明,这就是一个错误 所以这就是为什么...,只要包含对应头文件就可以 那么是不是只需要在b.h包含头文件a.h(#include”a.h)就可以 这是不可以因为a.h和a.cpp是没有关联:a.h只有a声明,没有a定义 如果此时运行的话...,在运行时会报错:缺少a定义 但是如果不运行是不会报错因为a.h虽然没有a定义,但是有声明,并没有语法和逻辑错误 缺少a定义是属于编译错误 正确做法是b.h包含a.cpp不是a.h 因为...因为链接 编译 编译是对项目中所有的源文件(注意只是源文件,不是头文件)进行编译,将它们“翻译”成为机器能识别的机器语言,每个源文件被编译后会生成一个对应目标文件,里面是源文件代码被翻译成机器语言

65720

C语言中.h和.c文件解析

main在运行时就会找到这个定义了这个函数aaa.c文件。   这是因为:   main函数为标准C/C++程序入口,编译器先找到该函数所在文件。   ...为什么经常见 xx.c 里面 include 对应 xx.h?   如果 a.c 不写,那么编译器不是自动把 .h 文件里面的东西跟同名 .c 文件绑定在一起?...用户只需要按照头文件接口声明来调用库功能,而不必关心接口怎么实现编译器从库中提取相应代码。   (2)头文件能加强类型安全检查。...这也正说明了,为什么很多编译器并不care到底这个文件后缀名是什么----因为#include预处理就是完成了一个"复制并插入代码"工作。   ...(非常重要)   VC,一帮情况下不需要自己写makefile,只需要将需要文件都包括project,VC自动帮你把makefile写好。

2.9K40

【c++入门】内联函数 和 函数重载 详解!

前言 在上一篇文章我们了解到了命名空间和缺省函数存在,以及C++为什么要引入它们。本章将继续C++C语言上扩展出语法,以帮助大家快速入门。函数竟然还有内联形式?函数重载,重载是什么?...☁️C++支持函数重载原理 C++支持函数重载,但是C语言不支持函数重载,这是为什么呢? C/C++,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接 。...实际项目通常是由多个头文件和多个源文件构成,当前a.cpp调用了b.cpp定义Add函数时,编译后链接前,a.o目标文件没有Add函数地址,因为Add是b.cpp定义,所以Add地址...C语言编译后:函数名字修饰没有发生改变。 C++编译后:函数名字修饰发生改变,编译器将函数参数类型信息添加到修改后名字。 以上我们可以看出gcc函数修饰后名字不变。...而g++函数修饰后变成【_Z+函数长度+函数名+类型首字母】。 这里就理解了C语言没办法支持重载,因为同名函数没办法区分。

13210

C++extern关键字知识点

3)、这提示我们,使用extern时候要严格对应声明时格式,实际编程,这样错误屡见不鲜。   ...但是在运行过程因为少了或者多了输入参数,往往照成系统错误,这种情况应该如何解决?   ...4 问题:extern “C”   C++环境下使用C函数时候,常常会出现编译器无法找到obj模块C函数定义,从而导致链接失败情况,应该如何解决这种情况呢?   ...答案与分析:   C++语言在编译时候为了解决函数多态问题,会将函数名和参数联合起来生成一个中间函数名称,而C语言则不会,因此造成链接时找不到对应函数情况,此时C函数就需要用extern “C...    然后把test1.cppg_str定义去掉,这个时候再编译连接test1和test2两个模块时,会报连接错误,这是因为你把全局变量g_str定义放在了头文件之后,test1.cpp这个模块包含了

97540

一个C#开发者重温C++心路历程

这是为什么呢? 只能推断,两个头文件string.h和iostream.h定义时,都定义命名空间std下了。而且,通过我后期使用,发现还有好多类和类型也定义std下了。...首先,缺失基础类型这种事,就很奇怪,其次不是一个头文件东西,定义到一个命名空间下,也容易让人混乱。 不过,对于C++,这么做好像已经是最优解了。...对此,我只能说,为什么这么麻烦!!! 以为这就很麻烦了吗?NO!!!还有更麻烦。 比如,我想在我定义结构体里使用自身类型,要怎么定义呢?...因为C++里,变量定义必须按照先声明后使用【绝对顺序】,那么,定义时就使用自身类型编译器提示错误。...不过C++因为,引用困难原因(上面已经描述了,只能引用其他.cpp文件对应头文件,并且,.cpp实现变量,还得头文件里外部声明一下),所以类定义写法也发生了改变。

81330

gcc和g++是什么,有什么区别?

那么,已编辑好 C 语言或者 C++ 代码前提下,如何才能调用 GCC 编译器为我们编译程序呢?...需要强调一点是,这并不是 gcc 和 g++ 区别,gcc 指令也可以用来编译 C++ 程序,同样 g++ 指令也可以用于编译 C 语言程序。 那么,gcc 和 g++ 区别是什么呢?...可以这样理解,gcc 是 GCC 编译器通用编译指令,因为根据程序文件后缀名,gcc 指令可以自行判断出当前程序所用编程语言类别,比如: xxx.c:默认以编译 C 语言程序方式编译此文件; xxx.cpp...也就是说,对于 .c 文件来说,gcc 指令以 C 语言代码对待,而 g++ 指令以 C++ 代码对待。但对于 .cpp 文件来说,gcc 和 g++ 都会以 C++ 代码方式编译。...严格来说,C++ 标准和 C 语言标准语法要求是有区别的。

1.1K10

C++从入门到精通——函数重载

<< "f(char b, int a)" << endl; } 注意:像这种有歧义输入编译器是会报错因为编译器不知道你是想要字符型还是ASCII码 三、函数重载具体代码展示 main.cpp...调用了b.cpp定义Add函数时】,编译后链接前,a.o目标文件没有Add函数地址,因为Add是b.cpp定义,所以Add地址b.o。...ps:出现上述情况原因就是因为编译器链接过程没有找到函数地址,我们可以检查是不是自己函数写错了 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?...采用C语言编译器编译后结果 结论:linux下,采用gcc编译完成后,函数名字修饰没有发生改变。...采用C++编译器编译后结果 结论:linux下,采用g++编译完成后,函数名字修饰发生改变,编译器将函数参数类型信息添加到修改后名字

13710

谈谈C++新标准带来属性(Attribute)

但是Attribute语法强烈依赖于各大编译器具体实现,彼此之间并不兼容,甚至部分关键属性导致了语言分裂,最终都会让使用者无所适从。所以C++11标准,特意提出了C++语言内置属性概念。...所以例子第一个函数func1才是正确无返回函数一个例子;而func2参数值为false情况下,它还是一个返回函数。...,所以一定要避免这种情况我们代码中出现。...(我gcc11编译器环境下尝试过几次,情况是什么都不发生,但是无法保证这是确定行为。)...get_important_ref(); // 此处因为不是按值返回nodiscard类型,不会有警告。 get_important_ptr(); // 同上原因,不会有警告。

56720

编程语言变革者 | 敢于打造理想世界 Rust

这是因为 ,Vec 是默认存储堆内存。假设,如果不发生所有权转移的话,栈内存上面,将出现同时指向 Vec 堆内存两个指针,就会发生「双重释放」同一块内存区域安全问题。...这是因为,first 是对 x 引用,但是 x 所有权已经被转移了,first 就自动失效。 如果 first 没有自动失效会发生什么呢?它可能变成一个野指针。 ? Rust 绑定默认不可变。...试想一下,如果这行代码通过编译,会发生什么事情?主线程中最后会调用 pop 方法,但是因为线程间其实并不同步, pop出来元素很可能不是 42 。这是线程不安全代码。...这也体现了 Rust 错误处理一种精致哲学,不像其他语言那样只用一个异常就包括了开发过程所有问题。 Rust 里,你可以分情况用不同类型来处理错误。...如果你不得不学其他语言,有了这些基础,你很快掌握其他语言。并且, Rust 编译器打磨下,你能拥有一个良好思维习惯。 4. 成为更好开发者。 ? 为什么这么说呢?

2.2K40
领券