.h和.cpp

而用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.14*r*r;
}

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

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

 

#include <iostream>
#include 
"Circle.h"
using namespace std;

int main()
{
    Circle c(
3);
    cout
<<"Area="<<c.Area()<<endl;
    
return 1;
}

    注意到开头时有#include "Circle.h"的声明,证明我们使用到了我们刚才写的Circle类。

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

    运行一下,输出结果为:

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

<think>我们正在讨论C++中的头文件(.h)源文件(.cpp/.cc)的区别用途。注意,.cpp.cc都是源文件的扩展名,功能上没有区别,只是不同编译器或平台的约定不同。关键点:1.头文件(.h)通常包含:-类声明(包括成员函数成员变量的声明)-函数声明(函数原型)-模板定义(因为模板需要在编译时实例化,所以通常放在头文件中)-内联函数定义-宏定义-类型定义(typedef,using)-全局变量的声明(使用extern关键字)-常量定义(如果需要在多个文件中共享,通常放在头文件中,但注意避免重复定义)2.源文件(.cpp/.cc)通常包含:-类成员函数的定义-函数的实现-全局变量的定义-只在本文件使用的静态函数静态变量3.为什么分开?-分离接口实现:头文件是接口,源文件是实现。-提高编译效率:头文件可以被多个源文件包含,而源文件单独编译。如果修改了实现(源文件),只需重新编译该源文件并重新链接,而不需要重新编译所有文件。-避免重复定义:将定义放在源文件中,而头文件中只有声明,可以避免链接时出现重复定义的错误。4.包含关系:-源文件包含头文件(使用#include指令)来获取声明。-头文件通常使用头文件保护(headerguards)或#pragmaonce来防止重复包含。5.编译过程:-每个源文件(.cpp/.cc)单独编译成一个目标文件(.o或.obj)。-链接器将多个目标文件以及库文件链接成可执行文件或库。6.扩展名差异:-.h是头文件的标准扩展名。-.cppC++源文件最常见的扩展名(尤其在WindowsMSVC环境中)。-.cc是另一种常见的C++源文件扩展名(常见于Unix/Linux系统,如GCC)。-其他扩展名:.cxx,.C(大写C)等,但较少见。7.注意事项:-头文件中应避免包含函数或变量的定义(除非是内联函数、模板、常量表达式等),否则当多个源文件包含该头文件时,会导致重复定义错误。-在头文件中使用extern声明全局变量,然后在某一个源文件中定义该全局变量。示例:假设我们有一个简单的类:头文件(mylib.h):```cpp#ifndefMYLIB_H//头文件保护#defineMYLIB_H//类声明classMyClass{public:voidprint();private:intdata;};//函数声明voidmyFunction();//全局变量声明(使用extern)externintglobalVar;//内联函数定义inlineintadd(inta,intb){returna+b;}#endif//MYLIB_H```源文件(mylib.cpp):```cpp#include"mylib.h"#include<iostream>//类成员函数定义voidMyClass::print(){std::cout<<"HellofromMyClass!"<<std::endl;}//函数定义voidmyFunction(){std::cout<<"HellofrommyFunction!"<<std::endl;}//全局变量定义intglobalVar=100;```另一个源文件(main.cpp):```cpp#include"mylib.h"intmain(){MyClassobj;obj.print();myFunction();globalVar=200;//使用全局变量intsum=add(1,2);//调用内联函数return0;}```编译过程:-编译mylib.cpp->mylib.obj-编译main.cpp->main.obj-链接mylib.objmain.obj生成可执行文件8.头文件中的模板内联函数:由于模板需要在编译时生成代码,所以模板的定义通常放在头文件中。内联函数也必须在每个使用它的编译单元中可见,所以定义在头文件中。9.常量定义:在C++中,如果常量是整型或枚举类型,且使用常量表达式初始化,可以在头文件中定义(C++中这样的常量具有内部链接属性,不会导致重复定义)。但在C++11之后,更推荐使用constexpr,并且通常可以放在头文件中。10.总结:-头文件(.h):声明接口,供其他文件包含。-源文件(.cpp/.cc):定义实现,单独编译。注意事项:在大型项目中,合理的文件组织非常重要,遵循这些约定可以避免很多编译链接错误。生成相关问题:1.为什么在头文件中使用#ifndef#define?2.C++中,如何避免头文件被重复包含?3.模板类/函数的定义为什么通常放在头文件中?4.C++项目中,如何组织头文件源文件的结构?5.源文件扩展名.cpp.cc有什么区别?如何选择?</think>### C++.h、.cpp.cc文件的区别与作用解析 在C++项目中,源文件通常分为**头文件(.h)** **源文件(.cpp/.cc)** 两类,它们协同工作但职责不同。以下是详细对比: #### 1. **头文件(.h)** **核心作用**:声明接口,提供代码的**公共访问点** - 包含内容: - 函数/类的**声明**(非实现) - 宏定义(`#define`) - 类型定义(`typedef`、`using`) - 模板声明 - 内联函数定义 - `extern`变量声明 - **关键特性**: - 通过`#include`指令被多个源文件引用 - 必须使用**头文件保护**防止重复包含: ```cpp #ifndef MY_HEADER_H // 条件编译指令 #define MY_HEADER_H // 声明内容... #endif ``` - 不参与直接编译,仅作为声明模板 > **示例**:`math_utils.h` > ```cpp > #ifndef MATH_UTILS_H > #define MATH_UTILS_H > > // 函数声明 > double circleArea(double radius); > > // 类声明 > class Vector2D { > public: > Vector2D(double x, double y); > double magnitude() const; > private: > double x, y; > }; > > #endif > ``` #### 2. **源文件(.cpp/.cc)** **核心作用**:实现具体功能,包含**实际代码逻辑** - **.cpp.cc的区别**: | 扩展名 | 使用场景 | 编译器兼容性 | |--------|------------------------|--------------------| | `.cpp` | Windows/MSVC主流选择 | 所有平台支持 | | `.cc` | Unix/GCC传统约定 | 所有平台支持 | > *功能完全相同,仅历史惯例差异* - 包含内容: - 函数/类成员的**定义实现** - 全局变量定义 - 只在本文件使用的静态函数/变量 - **关键特性**: - 每个`.cpp/.cc`文件独立编译成目标文件(.obj/.o) - 通过`#include "xxx.h"`获取声明 - 链接器将多个目标文件合并为可执行文件 > **示例**:`math_utils.cpp` > ```cpp > #include "math_utils.h" > #include <cmath> > > // 实现头文件声明的函数 > double circleArea(double radius) { > return 3.14159 * radius * radius; > } > > // 实现类的构造函数 > Vector2D::Vector2D(double x, double y) : x(x), y(y) {} > > // 实现类方法 > double Vector2D::magnitude() const { > return std::sqrt(x*x + y*y); > } > ``` #### 3. **协作流程与编译原理** 1. **预处理阶段**: 编译器处理`#include`指令,将`.h`内容插入源文件 $$ \text{main.cpp} \xrightarrow{\text{#include "math_utils.h"}} \text{合并后的代码} $$ 2. **编译阶段**: 每个`.cpp/.cc`独立编译,生成目标文件: ```bash g++ -c main.cpp # 输出main.o g++ -c math_utils.cpp # 输出math_utils.o ``` 3. **链接阶段**: 链接器解析跨文件的引用: ```bash g++ main.o math_utils.o -o program ``` #### 4. **设计最佳实践** - **头文件设计原则**: - 只放**声明**,避免函数实现(内联函数除外) - 用`extern`声明全局变量,在`.cpp`中定义 - 模板定义必须放在头文件中(编译器需见完整定义) - **源文件设计原则**: - 一个`.h`文件对应一个`.cpp`文件(便于维护) - 避免`#include .cpp`文件 - 对私有实现使用匿名命名空间: ```cpp // file: internal.cpp namespace { // 仅本文件可见 void helper() { ... } } ``` #### 5. **常见错误与解决方案** | 错误类型 | 原因 | 解决方案 | |-------------------------|-------------------------------|------------------------------| | **重复定义错误** | 头文件中包含函数/变量定义 | 将定义移到`.cpp`文件 | | **未解析符号错误** | 声明未实现或链接遗漏源文件 | 检查所有`.cpp`是否参与编译 | | **循环包含错误** | A.h包含B.h,B.h又包含A.h | 使用前置声明代替包含 | > 通过合理分离声明(.h)与实现(.cpp/.cc),可显著提升编译效率——修改实现只需重新编译单个`.cpp`文件,而非整个项目[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值