首页 > 其他分享 >关于头文件

关于头文件

时间:2024-09-14 17:53:27浏览次数:3  
标签:Foo 头文件 定义 LIBRARY 关于 include TESTPROJ

提示:文章

文章目录

前言

前期疑问:
本文目标:


一、背景

2024年9月10日18:50:11

在看代码规范的时候又看到头文件相关的知识点,今天又再次整理一下,对头文件的使用又加深了理解。以前会莫名其妙报些告警,现在遇到应该会好处理了。

上述表述等于是将头文件中哪些可以定义哪些不能定义整理了一下
之前整理的文章是这一篇关于头文件,这次整理时也参考了这篇文章,而且再次看这篇文章的时候有了新的发现。真不错!**

二、

2.1 constexpr定义的变量是否可以放在头文件中

想把constexpr int STATISTICS_MAP_MAX_SIZE = 24 * 6 * 200;放在头文件中,就在疑惑能不能将const放在头文件中

1.在头文件中定义const类型的非static变量,在link时不会报错,这是因为const型的变量在complie时已经转换为常量,每个obj模块里有一个相同内容的常量,并不会有变量名冲突发生。

2.在头文件定义非const类型时,link时报错,这是因为这样相当于在每个包含该头文件的cc文件的全局域定义了一个名字相同的全局变量,虽然在complie时由于分开编译,不会产生错误,但是在link时,将会发生变量名冲突。

3.在头文件中使用extern 声明一个全局变量时可以的,而且是全局变量使用的推荐做法,前提是声明的这个全局变量,已经在一个cc文件,且只在一个cc文件中定义。

4.在头文件中定义的一个static变量,对于包含该头文件的所有源文件,实质上在每个源文件内定义了一个同名的static变量,他们仅为该源文件的全局变量而非全程序的全局变量。实际上在编译过程中,会为其每个obj模块中编译产生不同的名字,所以在link时候不会报错。但这样相当于在每个obj模块中有一个的static变量,它实际上是独立在每个obj模块中的文件级全局变量,不是全程序域的全局变量。

所以,在头文件中,最好只声明而不定义,定义也仅仅定义const型变量

来源:C语言头文件里可不可以定义变量

2.2 结构体是否可以定义在头文件中

总之,结构体的定义位置根据项目的需要和结构体的使用范围来决定。如果需要在多个源文件中共享结构体定义,则应该放在头文件中;如果仅在一个源文件中使用,则可以直接在源文件中定义。

今天重新看了下之前的一篇文章,关于头文件,这篇文章之前是拷贝的其他文章,有缺失,但是今天看了下发现了一个知识点,即之前我在疑问,结构体能否写在头文件中,今天看到那篇文章写到。不能写在头文件中的是变量和函数。

当然结构体可以定义在头文件中,但是如果只有一个源文件使用,就可以写在源文件中。

关于头文件不能定义函数和变量,有几个例外,看到一个文章说的比较好:

参考《C++ Primer中文版》第四版 人民邮电出版社 P58:

因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。

对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已经知道的const对象和inline函数。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的。

在头文件中定义这些实体,是因为编译器需要它们的定义(不只是声明)来产生代码

类定义含类作用域内的函数与变量定义,也就是成员函数与数据成员。

1、内成员函数如果隐式内联了,当然没有问题,可以放在头文件中。否则,如果显示内联,可以放在头文件中。如若不然,放在头文件中可能引起重复定义。

2、数据成员:静态成员定义放在头文件中,可能会引起重复定义。非静态数据成员定义在类实例化的时候,才定义。

从根本上分析的话,还得从链接属性上分析。

上述表述等于是将头文件中哪些可以定义哪些不能定义整理了一下

三、头文件相互引用相关

3.1 禁止头文件相互引用

看代码规范,提到禁止头文件相互引用。之前好像遇到过头文件相互依赖,当时肯定很懵逼啊,而且在实际开发中遇到就很捉急。现在就此深化理解一下。

因为当时出现头文件循环依赖的时候并没有专门去记录。只是回退了操作。

现在参考规范,写了个循环依赖的示例。

代码如下:

// Foo.h文件

// Foo.h文件
#ifndef TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H
#define TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H

#include "Container.h"

class Foo {
    Container* container;
};

#endif //TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H

// Conatiner.h文件

// Conatiner.h文件
#ifndef TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H
#define TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H

#include <iostream>
#include <map>
#include "Foo.h"

class Container {
    std::map<std::string, Foo> table;
};

#endif //TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H

// main.cpp文件

#include "Foo.h"
#include "Container.h"

int main()
{
    Foo foo;
    Container container;
	std::cout << "program ok" << std::endl;
    return 0;
}

上述代码创建在一个工程中,会报错如下

D:/AppData/CLion/testProj_local_without_library/headFileInclude/Container.h:14:27: error: 'Foo' was not declared in this scope
   14 |     std::map<std::string, Foo> table;
      |                           ^~~
D:/AppData/CLion/testProj_local_without_library/headFileInclude/Container.h:14:30: error: template argument 2 is invalid

对于这个错误我一开始是不好理解的,查了资料

循环依赖是一种设计上的问题,需要避免。如果发生了循环依赖,编译器可能会报错或警告,并阻止代码的编译通过。解决循环依赖问题的最佳方法是重新设计代码结构,以消除循环依赖。

感觉编译器是禁止编译通过。、

这个问题怎么解决呢?使用类前置声明。

3.2 c++类前置声明

修改成下述形式:

// Foo.h文件

// Foo.h文件
#ifndef TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H
#define TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H

#include "Container.h"

class Foo {
    Container* container;
};

#endif //TESTPROJ_LOCAL_WITHOUT_LIBRARY_FOO_H

// Conatiner.h文件

// Conatiner.h文件
#ifndef TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H
#define TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H

#include <iostream>
#include <map>
//#include "Foo.h"

class Foo;

class Container {
    std::map<std::string, Foo> table;
};

#endif //TESTPROJ_LOCAL_WITHOUT_LIBRARY_CONTAINER_H

// main.cpp文件

#include "Foo.h"
#include "Container.h"

int main()
{
    Foo foo;
    Container container;
    std::cout << "program ok" << std::endl;
    return 0;
}

就可以成功编译程序并执行

然后突然意识到这是对于c++代码类的解决办法。如果是c语言循环依赖该怎么解决?

暂时先不管这个。先解决上述类的问题。

我想到的一个就是重新设计代码。


总结

未完待续

标签:Foo,头文件,定义,LIBRARY,关于,include,TESTPROJ
From: https://blog.csdn.net/2301_77560238/article/details/142106377

相关文章

  • 关于新版本 tidb dashboard API 调用说明
    作者:WalterWj背景tidb新版本增加了对登陆密码进行非对称加密的的步骤。token的获取可以参考登录界面的登录流程:通过api获取/api/user/login_info获取公钥等信息通过api/api/user/login进行登录,payload为username和password,如果第一步存在公钥,则需要使用公钥对pas......
  • 【关于c++模版类的报错问题】
    关于c++模版类的报错问题模版类的定义使用类模板模版类的定义通常在实现一个类时,会在.h头文件中声明函数,在.cpp文件中实现该函数。然而如果是模版类的话情况则会稍有不同。这是一个名为debug.h的头文件,里面包含了一个名为A的模版类类A的实现在debug.hpp中,模板类的......
  • 全能还是专精?关于技术通才与技术专家的思考
    在日新月异的IT行业中,每隔数年乃至数月,便会涌现出革新性的技术或前沿框架,引领行业潮流。比如前端开发,我刚开始工作时,大部分都是静态页面+JavaScript,页面上只有一些简单的交互。后来出现了Ajax技术和JQuery库,现在想起当年第一次使用JQuery时,真的觉得这就是前端库的终点。结果......
  • 这次终于搞清楚了,关于 protoc 工具的小问题
    protoc工具可以干什么?protoc工具可以通过相关插件将.proto文件编译成C、C++、Golang、Java、Python、PHP等多种语言的代码。本文主要讨论通过protoc生成Golang代码,例如我们常见的命令:protoc-I.--go_out=xxx想了解更多参数,执行protoc--help查看。遇到的小问题一、......
  • 中移ML307A(4G Cat1,C-SDK,OpenCPU)模组学习开发-关于操作SD卡
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/ML307A_OPEN"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 提示1,SD卡的引脚和串口0是冲突......
  • 【Python爬虫系列】_016.关于登录和验证码
    我的个人主页:......
  • C++入门基础知识65——【关于C++ 数据封装】
    成长路上不孤单......
  • C++入门基础知识66——【关于C++ 接口(抽象类)】
    成长路上不孤单......
  • 关于 wqs 二分的一言两语
    感觉一个比较入门的题目是P2619。你要求的是恰好有\(need\)条白色边的,这个很难表示,因为如果直接跑一遍MST,你不能保证一定选了\(need\)条,有可能白色边“太好了”或者“太坏了”。但是我们发现,如果白色边“越好”,就会尽可能选白色,反之亦然。也就是说如果我们增加一个费用:给所......
  • 关于排查GPTbot爬虫风险说明
    背景OpenAI在没有正式宣布的情况下,于本周发布了一项网站爬虫规范GPTbot。一旦被大模型爬虫爬取,也意味着你的数据无法从公共数据集中删除。例如比较有名的公共数据集CommonCrawl(翻译成中文是“常见爬取”或“通用爬虫”),常被用于训练OpenAI的ChatGPT。风险排查针对网络......