C++ 中头文件(.h)和源文件(.cpp)都应该写些什么

本贴最后更新于 1927 天前,其中的信息可能已经时过境迁

C++ 中头文件(.h)和源文件(.cpp)都应该写些什么
头文件(.h):

写类的声明(包括类里面的成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。

在写头文件时需要注意,在开头和结尾处必须按照如下样式加上预编译语句(如下):

#ifndef CIRCLE_H
#define CIRCLE_H

//你的代码写在这里

#endif

这样做是为了防止重复编译,不这样做就有可能出错。

至于CIRCLE_H这个名字实际上是无所谓的,你叫什么都行,只要符合规范都行。原则上来说,非常建议把它写成这种形式,因为比较容易和头文件的名字对应。

源文件(.cpp):

源文件主要写实现头文件中已经声明的那些函数的具体代码。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。那么当你需要用到自己写的头文件中的类时,只需要#include进来就行了。

下面举个最简单的例子来描述一下,咱就求个圆面积。

 第1步,建立一个空工程(以在VS2003环境下为例)。

 第2步,在头文件的文件夹里新建一个名为Circle.h的头文件,它的内容如下:

#ifndef CIRCLE_H
#define CIRCLE_H

class Circle
{
private:
double r;//半径
public:
Circle();//构造函数
Circle(double R);//构造函数
double Area();//求面积函数
};

#endif
注意到开头结尾的预编译语句。在头文件里,并不写出函数的具体实现。

第3步,要给出Circle类的具体实现,因此,在源文件夹里新建一个Circle.cpp的文件,它的内容如下:

#include "Circle.h"

Circle::Circle()
{
this->r=5.0;
}

Circle::Circle(double R)
{
this->r=R;
}

double Circle:: Area()
{
return 3.14rr;
}
需要注意的是:开头处包含了 Circle.h,事实上,只要此 cpp 文件用到的文件,都要包含进来!这个文件的名字其实不一定要叫 Circle.cpp,但非常建议 cpp 文件与头文件相对应。

最后,我们建一个main.cpp来测试我们写的Circle类,它的内容如下:

#include
#include "Circle.h"
using namespace std;

int main()
{
Circle c(3);
cout<<"Area="<<c.Area()<<endl;
return 1;
}
注意到开头时有#include "Circle.h"的声明,证明我们使用到了我们刚才写的 Circle 类。

至此,我们工程的结构为:

运行一下,输出结果为:

说明我们写的 Circle 类确实可以用了。

1..h 叫做头文件,它是不能被编译的。“#include”叫做编译预处理指令,可以简单理解成,在 1.cpp 中的#include"1.h"指令把 1.h 中的代码在编译前添加到了 1.cpp 的头部。每个.cpp 文件会被编译,生成一个.obj 文件,然后所有的.obj 文件链接起来你的可执行程序就算生成了。

发现了没有,你要在.h 文件中严格区分声明语句和定义语句。好的习惯是,头文件中应只处理常量、变量、函数以及类等等等等的声明,变量的定义和函数的实现等等等等都应该在源文件.cpp 中进行。

至于.h 和.cpp 具有同样的主文件名的情况呢,对编译器来讲是没有什么意义的,编译器不会去匹配二者的主文件名,相反它很傻,只认#include 等语句。但是这样写是一种约定俗成的编程风格,一个类的名字作为其头文件和源文件的主文件名比如 Class1.h 和 Class1.cpp,这个类的声明在 Class1.h 中,实现在 Class1.cpp 中,我们人类看起来比较整齐,读起来方便,也很有利于模块化和源代码的重用。

为什么这个风格会约定俗成?有一句著名的话,叫“程序是为程序员写的”。

2.h 文件和 cpp 文件也就是说,在 h 文件中声明 Declare,而在 cpp 文件中定义 Define。 “声明”向计算机介绍名字,它说,“这个名字是什么意思”。而“定义”为这个名字分配存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”处分配存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生的指针。定义也可以是声明。如果该编译器还没有看到过名字 A,程序员定义 int A,则编译器马上为这个名字分配存储地址。声明常常使用于 extern 关键字。如果我们只是声明变量而不是定义它,则要求使用 extern。对于函数声明, extern 是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明。

另篇:

在 C++ 编程过程中,随着项目的越来越大,代码也会越来越多,并且难以管理和分析。于是,在 C++ 中就要分出了头(.h)文件和实现(.cpp)文件,并且也有了 Package 的概念。

对于以 C 起步,C#作为“母语”的我刚开始跟着导师学习 C++ 对这方面还是感到很模糊。虽然我可以以 C 的知识面对 C++ 的语法规范,用 C#的思想领悟 C++ 中类的使用。但是 C#中定义和实现是都在一个文件中(其实都是在类里面),而使用 C 的时候也只是编程的刚刚起步,所写的程序也只要一个文件就够了。因此对于 C++ 的 Package 理解以及.h 文件和.cpp 文件的总是心存纠结。

幸好导师有详细的 PPT 让我了解,一次对于 Package 的认识就明白多了。简单讲,一个 Package 就是由同名的.h 和.cpp 文件组成。当然可以少其中任意一个文件:只有.h 文件的 Package 可以是接口或模板(template)的定义;只有.cpp 文件的 Package 可以是一个程序的入口。

当然更具体详细的讲解,欢迎下载导师的教学 PPT-Package 来了解更多。

不过我在这里想讲的还是关于.h 文件和.cpp 文件

知道 Package 只是相对比较宏观的理解:我们在项目中以 Package 为编辑对象来扩展和修正我们的程序。编写代码时具体到应该把什么放到.h 文件,又该什么放在.cpp 文件中,我又迷惑了。

虽然 Google 给了我很多的链接,但是大部分的解释都太笼统了:申明写在.h 文件,定义实现写在.cpp 文件。这个解释没有差错,但是真正下手起来,又会发现不知道该把代码往哪里打。

于是我又把这个问题抛给了导师,他很耐心地给我详详细细地表述了如何在 C++ 中进行代码分离。很可惜,第一次我听下了,但是没有听太懂,而且本来对 C++ 就了解不深,所以也没有深刻的印象。

经过几个项目的试炼和体验之后,我又拿出这个问题问导师,他又一次耐心地给我讲解了一遍(我发誓他绝对不是忘记了我曾经问过同样的问题),这次我把它记录了下来。

为了不再忘记,我将它们总结在这里。

概览
非模板类型(none-template) 模板类型(template)
头文件(.h)
全局变量申明(带 extern 限定符)
全局函数的申明
带 inline 限定符的全局函数的定义
带 inline 限定符的全局模板函数的申明和定义
类的定义
类函数成员和数据成员的申明(在类内部)
类定义内的函数定义(相当于 inline)
带 static const 限定符的数据成员在类内部的初始化
带 inline 限定符的类定义外的函数定义
模板类的定义
模板类成员的申明和定义(定义可以放在类内或者类外,类外不需要写 inline)
实现文件(.cpp)
全局变量的定义(及初始化)
全局函数的定义
(无)
类函数成员的定义
类带 static 限定符的数据成员的初始化
*申明:declaration
*定义:definition

头文件
头文件的所有内容,都必须包含在

#ifndef {Filename}
#define {Filename}

//{Content of head file}

#endif
这样才能保证头文件被多个其他文件引用(include)时,内部的数据不会被多次定义而造成错误

inline 限定符
在头文件中,可以对函数用 inline 限定符来告知编译器,这段函数非常的简单,可以直接嵌入到调用定义之处。

当然 inline 的函数并不一定会被编译器作为 inline 来实现,如果函数过于复杂,编译器也会拒绝 inline。

因此简单说来,代码最好短到只有 3-5 行的才作为 inline。有循环,分支,递归的函数都不要用做 inline。

对于在类定义内定义实现的函数,编译器自动当做有 inline 请求(也是不一定 inline 的)。因此在下边,我把带有 inline 限定符的函数成员和写在类定义体内的函数成员统称为“要 inline 的函数成员”

非模板类型
全局类型

就像前面笼统的话讲的:申明写在.h 文件。

对于函数来讲,没有实现体的函数,就相当于是申明;而对于数据类型(包括基本类型和自定义类型)来说,其申明就需要用 extern 来修饰。

然后在.cpp 文件里定义

  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 286 关注
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    9 引用 • 32 回帖 • 179 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 596 关注
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 21 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 232 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 636 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 496 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 2 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    180 引用 • 400 回帖
  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    89 引用 • 113 回帖
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 247 回帖 • 212 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 17 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 689 关注
  • gRpc
    10 引用 • 8 回帖 • 48 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    9 引用 • 83 回帖
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    160 引用 • 470 回帖
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖 • 1 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 8 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 170 关注
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    261 引用 • 662 回帖 • 2 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 105 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    126 引用 • 3621 回帖
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 166 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    138 引用 • 268 回帖 • 219 关注