为什么不推荐在头文件中直接定义函数?

这篇具有很好参考价值的文章主要介绍了为什么不推荐在头文件中直接定义函数?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么不推荐在头文件中直接定义函数?

1. 函数的分文件编写

在C++中,函数的分文件编写是一种让代码结构更加清晰的方法,通常可以分为以下几个步骤:

  • 创建后缀名为 .h 的头文件,在头文件中写函数的声明,以及可能用到的其他头文件或命名空间
  • 创建后缀名为 .cpp 的源文件,在源文件中写函数的定义,同时引入自定义头文件,将头文件与源文件绑定
  • 在需要使用函数的地方,引入自定义头文件,然后直接调用函数,无需再写函数的实现

例如,如果要编写一个求两个数最大值的函数,可以这样做:

  • 创建一个 max.h 头文件,在其中写入以下内容:
#pragma once // 防止头文件重复包含
#include <iostream> // 引入输入输出流头文件
using namespace std; // 使用标准命名空间
// 函数声明
int max(int a, int b);
  • 创建一个 max.cpp 源文件,在其中写入以下内容:
#include "max.h" // 引入自定义头文件
// 函数定义
int max(int a, int b) {
    return a > b ? a : b; // 三目运算符,返回最大值
}
  • 在需要使用函数的地方,例如 main.cpp 文件中,引入自定义头文件,并调用函数:
#include "max.h" // 引入自定义头文件
int main() {
    int a = 10;
    int b = 20;
    cout << "The max of " << a << " and " << b << " is " << max(a, b) << endl; // 调用函数并输出结果
    system("pause"); // 暂停程序
    return 0;
}

文件结构如图所示:

为什么不推荐在头文件中直接定义函数?

2. 头文件中不推荐直接定义函数

在头文件中直接写函数的定义是不推荐的,有以下几个原因:

  • 在头文件中写函数的定义会导致重复定义的错误,如果这个头文件被多个源文件包含。因为每个源文件都会把头文件的内容复制过来,相当于在多个地方定义了同一个函数,这违反了单定义原则
  • 在头文件中写函数的定义会增加编译的时间,如果这个头文件被频繁修改。因为每次修改头文件后,所有包含这个头文件的源文件都需要重新编译,这对于大型项目来说非常耗时
  • 在头文件中写函数的定义会降低代码的可读性和可维护性,如果这个头文件包含了很多函数的定义。因为头文件的主要作用是提供函数的声明和接口,而不是实现细节。把函数的定义放在源文件中,可以让代码结构更清晰,也便于隐藏实现细节和保护数据

2.1 单定义原则

在头文件中写函数的定义会导致重复定义的错误,如果这个头文件被多个源文件包含。比如,假设有一个头文件 max.h,其中定义了一个求两个数最大值的函数:

// max.h
#pragma once
#include <iostream>
using namespace std;

int max(int a, int b) {
    return a > b ? a : b;
}

然后,有两个源文件 main1.cppmain2.cpp,都包含了这个头文件,并且都调用了这个函数:

// main1.cpp
#include "max.h"

int foo() {
    cout << "The max of 10 and 20 is " << max(10, 20) << endl;
    return 0;
}
// main2.cpp
#include "max.h"

int main() {
    cout << "The max of 30 and 40 is " << max(30, 40) << endl;
    return 0;
}

为什么不推荐在头文件中直接定义函数?

看到这里可能会有个疑问,编译的时候 main1.cpp 调用 max.h 中的函数,但是 main2.cpp 中的主函数中没有调用 main1.cpp 中的函数,为什么还是会编译不通过呢?两个不同的文件定义同一个函数也会冲突吗?即使其中一个文件和另一个文件没有任何关系?

编译时,每个源文件会生成一个目标文件,然后链接生成可执行文件。即使 main2.cpp 没有调用 main1.cpp 的函数,但 main1.cpp 中包含了 max.h,相当于在 main1.cpp 中定义了max函数,与 main2.cpp 中的max函数冲突。当链接时,如果出现同名的函数,就会出现重复定义的错误。因此,每个函数应该只在一个源文件中定义,或者使用命名空间或静态修饰符来避免冲突


为了解决这个问题,我们应该把函数的定义放在另一个源文件 max.cpp 中,然后在头文件中只声明函数:

// max.h
#pragma once
#include <iostream>
using namespace std;

int max(int a, int b); // 函数声明
// max.cpp
#include "max.h"
int max(int a, int b) { // 函数定义
    return a > b ? a : b;
}

为什么不推荐在头文件中直接定义函数?

这样就可以避免重复定义的错误了


2.2 减少编译时间

在头文件中写函数的定义会增加编译的时间,如果这个头文件被频繁修改。比如,假设有一个头文件 math.h,其中定义了一些数学相关的函数:

// math.h
double sin(double x) {
    // some code to calculate sin(x)
}

double cos(double x) {
    // some code to calculate cos(x)
}

double tan(double x) {
    // some code to calculate tan(x)
}

然后,有很多源文件都包含了这个头文件,并且都调用了这些函数。如果我们想要修改或添加某个函数的实现细节,比如改进 sin 函数的算法,那么我们就需要修改头文件 math.h。但是这样一来,所有包含了这个头文件的源文件都需要重新编译,因为它们都依赖于头文件的内容。这对于大型项目来说非常耗时。为了解决这个问题,我们应该把函数的定义放在另一个源文件 math.cpp 中,然后在头文件中只声明函数:

// math.h
double sin(double x); // 函数声明
double cos(double x); // 函数声明
double tan(double x); // 函数声明
// math.cpp
#include "math.h"
double sin(double x) { // 函数定义
    // some code to calculate sin(x)
}

double cos(double x) { // 函数定义
    // some code to calculate cos(x)
}

double tan(double x) { // 函数定义
    // some code to calculate tan(x)
}

这样就可以减少编译的时间了,因为只有修改或添加了函数的源文件才需要重新编译

简单来说,分为两种情况

  • 第一种:在头文件中定义函数。如果有很多源文件都引用了这个头文件,那么当头文件修改后,所有引用头文件的源文件都要重新编译,对于大型项目非常耗时

  • 第二种:把函数的定义和声明放在不同的文件中。这样做可以使得当源文件中定义的函数发生修改时,只需要重新编译被修改的源文件就可以了,不需要所有引用这个头文件的源文件重新编译,节省了非常多的时间


为什么在头文件中定义的函数发生改变时,所有包含该头文件的源文件需要重新编译?

还是借用以上的例子,我的猜想是这样的

假如在 main.cpp 源文件中引用 math.h 头文件,相当于把头文件中的内容复制到了源文件里

那么如果 math.h 头文件中定义函数,并且 main.cpp 源文件中引用了 math.h 头文件,则相当于把 math.h 中的定义的函数复制到 main.cpp 源文件里,一旦头文件中的函数发生改变,那么就相当于源文件发生了改变

因此所有包含 math.h 头文件的源文件都需要重新编译

此外,多个源文件包含同一个定义函数的头文件,会导致重定义的错误。这里只是举个例子假设编译器允许这样的操作,实际上编译不会通过

为什么不推荐在头文件中直接定义函数?

调用函数时的索引顺序:

在源文件中调用函数的时候,是先到头文件里找声明的函数,然后再通过链接的过程找到对应的源文件里的函数

如下图所示,main.cpp 调用函数时,先到 math.h 中找到声明的函数,然后再通过链接的过程找到对应的源文件 math.cpp 里的函数

为什么不推荐在头文件中直接定义函数?

这个过程可以看作是查字典,头文件相当于目录,对应着每个函数所在的位置


2.3 可读性与安全性

在头文件中写函数的定义会降低代码的可读性和可维护性,如果这个头文件包含了很多函数的定义。比如,假设有一个头文件 utils.h,其中定义了一些工具类的函数:

// utils.h
#include <string>
#include <vector>
using namespace std;

string trim(string s) {
    // some code to trim the whitespace of s
}

vector<string> split(string s, char delim) {
    // some code to split s by delim
}

string join(vector<string> v, char delim) {
    // some code to join v by delim
}

bool is_number(string s) {
    // some code to check if s is a number
}

int to_int(string s) {
    // some code to convert s to int
}

string to_string(int x) {
    // some code to convert x to string
}

这个头文件包含了很多函数的定义,这会让代码看起来很冗长,也不容易找到想要的函数。而且,如果我们想要修改或添加某个函数的实现细节,比如改进 trim 函数的效率,那么我们就需要修改头文件 utils.h。但是这样会影响到所有包含了这个头文件的源文件,也会增加代码的复杂度和出错的风险。为了解决这个问题,我们应该把函数的定义放在另一个源文件 utils.cpp 中,然后在头文件中只声明函数:文章来源地址https://www.toymoban.com/news/detail-569557.html

// utils.h
#include <string>
#include <vector>
using namespace std;

string trim(string s); // 函数声明
vector<string> split(string s, char delim); // 函数声明
string join(vector<string> v, char delim); // 函数声明
bool is_number(string s); // 函数声明
int to_int(string s); // 函数声明
string to_string(int x); // 函数声明
// utils.cpp
#include "utils.h"

string trim(string s) {
    // some code to trim the whitespace of s
}

vector<string> split(string s, char delim) {
    // some code to split s by delim
}

string join(vector<string> v, char delim) {
    // some code to join v by delim
}

bool is_number(string s) {
    // some code to check if s is a number
}

int to_int(string s) {
    // some code to convert s to int
}

string to_string(int x) {
    // some code to convert x to string
}

到了这里,关于为什么不推荐在头文件中直接定义函数?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 为什么我不再推荐枚举策略模式

    一、为什么讲策略模式 二、经典策略模式 三、基于枚举的策略模式 四、基于工厂的策略模式 策略模式,应该是工作中比较常用的设计模式,调用方自己选择用哪一种策略完成对数据的操作,也就是“一个类的行为或其算法可以在运行时更改” 我个人的理解是 将一些除了过

    2024年02月07日
    浏览(42)
  • MySQL为什么不推荐使用in

    有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准 https://blog.zysicyj.top 首发博客地址 系列文章地址 当使用IN语句时,MySQL可能会遇到以下问题: 索引问题:MySQL使用索引来加速查询,但在使用IN语句时,MySQL可能无法有效地使用索引。这是因为

    2024年02月09日
    浏览(44)
  • Linux中vim为什么直接可以操作jar包

    今天上线遇到了令人迷惑的问题,宽哥大佬排查的时候用vim直接查看了jar包,感觉很神奇,所以查了查资料; 学无止境!! JAR 文件的本质: JAR(Java ARchive)文件是 Java 平台上用于封装 Java 类文件、相关的元数据和资源文件的压缩包。JAR 文件使用了 ZIP 文件格式进行压缩和存

    2023年04月26日
    浏览(60)
  • 记录--为什么推荐用svg而不用icon?

    使用背景: 1.因为svg图标在任何设备下都可以高清显示,不会模糊。而icon会在显卡比较低的电脑上有显示模糊的情况 2.svg图标在页面render时 速度会比icon稍微快一点 3.实现小程序换肤功能 ;方案见:www.yuque.com/lufeilizhix… SVG基础可参考:www.yuque.com/lufeilizhix… inline svg是目前前

    2024年02月08日
    浏览(50)
  • 为什么不推荐使用Lombok?@Data不香吗?

    目录 一、前言 二、源码跟踪 三、总结 之前写项目遇到的一个Bug,下面是模拟代码。 新建一个springboot的项目,Person一个实体类,定义一个方法传一个JSON数据 springboot启动之后postman发送一次请求。 请求路径:http://localhost:8080/user JSON数据: 后台输出结果 我们会发现,aName字段

    2024年02月09日
    浏览(50)
  • 【Unity细节】为什么加载精灵图集直接导致Unity引擎崩溃

    👨‍💻个人主页 :@元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 😶‍🌫️收录于专栏 :unity细节和bug 😶‍🌫️优质专栏 ⭐【软件设计师高频考点暴击】 点击运行,Unity直接崩溃退出 在加载打包后的图集时,可能图集里面没有相应的资源,

    2024年02月05日
    浏览(57)
  • Java开发手册中为什么禁止使用BigDecimal的equals方法做等值比较已经为什么禁止使用double直接构造BigDecimal

    阿里Java开发手册嵩山版中明确指出: 1、BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法 equals()方法会比较值和精度(1.0与1.00返回结果为false),而compareTo()则会忽略精度 2、禁止使用构造方法BigDecimal(double)的方式把double值转换为BigDecimal对象 BigDecimal(double)存在精度损

    2024年02月07日
    浏览(54)
  • 为什么单片机不能直接驱动继电器和电磁阀?

    在开始前我有一些资料,是我根据网友给的问题精心整理了一份「单片机的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!! 这就涉及到一个功率的问题, 单片机的IO口通常电流都特别小,因为单片机的芯片不需

    2024年01月22日
    浏览(50)
  • 【Spring】浅谈spring为什么推荐使用构造器注入

    因本人实力有限,该文章主要内容(在文章基础上加了点点东西)均来自: 原文链接:https://www.cnblogs.com/joemsu/p/7688307.html 作者:joemsu ​ Spring框架对Java开发的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反转)和AOP,平时使用最多的就是其中的IOC,我们通过

    2024年02月13日
    浏览(45)
  • Vue3为什么推荐使用ref而不是reactive

    reactive 本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代 option api 的 data 的替代,可以存放任何数据类型,而 reactive 声明的数据类型只能是对象; 先抛出结论,再详细说原因:非必要不用 reactive ! (官方文档也有对应的推荐

    2024年02月07日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包