300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 关于C++ .h文件和.cpp文件的知识梳理

关于C++ .h文件和.cpp文件的知识梳理

时间:2019-10-22 05:35:08

相关推荐

关于C++ .h文件和.cpp文件的知识梳理

C++ 关于.h文件和.cpp文件的知识梳理(1)

1. C++程序的编译过程2. #pragma once 有什么作用3. 在头文件中定义static变量和const常量4.正确声明和定义变量的方法--extern

1. C++程序的编译过程

C++程序的编译过程可以分为四个步骤,分别为预处理,编译汇编链接。他们的工作内容分别为:

预处理:展开头文件,去除源代码中的注释等。编译: 将预处理后的程序转换成汇编代码。汇编: 将汇编代码进一步转换成二进制机器码。链接:将多个目标文件及其所需的库文件链接生成可执行文件。

其中我们关注最多的是预处理链接,头文件和预处理息息相关,而声明与定义的分离则和链接有着密切关系。

2. #pragma once 有什么作用

我们都知道#pragma once还有类似" #ifndef “、” #endif "的作用都是保护你的头文件只被目标文件加载一次,但这个保护的作用能有多大呢?首先我们需要知道头文件的作用,#include 一个文件(无论.h或是.cpp),仅仅是将文件中的代码复制一份到你的代码之中,头文件(.h)仅在编译过程中的预处理中起到作用。知道了这个后我们便可以开始进行一些测试了。

测试样例一

代码:

//head.hint a = 10;//main.cpp#include <iostream>#include "head.h"#include "head.h"//重复导入int main(){std::cout<<"a = "<<a<<std::endl;std::cout<<"a's address = "<<&a<<std::endl;return 0;}

执行结果:

In file included from C:head.h:2:5: error: redefinition of 'int a'int a = 10;^In file included from C:head.h:2:5: note: 'int a' previously defined hereint a = 10;

结果报错了,编译器提示错误,重复定义了int a变量,这是因为我们#include “head.h” 两次,相当于在我们的main.cpp中加了两行int a = 10。我们在head.h文件中加上#pragma once 可以再运行试试。

//head.h#pragma onceint a = 10;//main.cpp内容不变

执行结果:

a = 10a's address = 0x40a010

程序编译通过了,因为#pragma once 让头文件只被导入了一次,但一个头文件往往不止会被一个源文件include,也有可能被多个文件include,到时候我们的写法还对吗?#pragma once 还能保证我们的程序安全,不会产生重复定义吗?我们来看接下来的示例。

测试样例2

代码:

//head.h#pragma once#include <iostream>int a = 10;void fun1();void fun2();//fun1.cpp#include "head.h"void fun1(){std::cout<<"this is fun1"<<std::endl;std::cout<<"a = "<<a << std::endl;std::cout<<"a's = "<<&a <<std::endl;}//fun2.cpp#include "head.h"void fun2(){std::cout<<"this is fun2"<<std::endl;std::cout<<"a = "<<a << std::endl;std::cout<<"a's = "<<&a <<std::endl;}//main.cpp#include "head.h"int main(){fun1();fun2();return 0;}

执行结果:

C:head.h:4: multiple definition of `a'C:head.h:4: first defined hereC:head.h:4: multiple definition of `a'C:head.h:4: first defined here

结果编译器报错了,因为在fun1.cpp 和fun2.cpp中对变量 a 进行了重复的定义,显然当有多个文件的时候#pragma once 并不能保证我们的程序中只include 该文件一次。也正是因为这个原因,才会有我们后面的规定–仅在.h 文件中进行变量和函数的声明而不进行定义(存在一些特例,比如class 的内敛成员函数,模板函数), 因为函数与变量的声明可以进行任意多次,而定义只能有一次。

总结:#pragma once 仅能保证在单一文件中对于头文件的include只会进行一次。

3. 在头文件中定义static变量和const常量

static 关键字作用于变量时无非两个作用,一个是延长变量的生命周期,另一个是限定变量仅能在定义的该变量的文件中使用。如果我们在头文件中定义static 变量会发生什么?还有很多人会喜欢在头文件中加一些const 常量,这些都是一些错误的用法。

代码:

//head.h#pragma once#include <iostream>const int a = 10;static int s = 9;void fun1();void fun2();//fun1.cpp#include "head.h"void fun1(){std::cout<<"this is fun1:"<<std::endl;std::cout<<"a = "<<a <<" a's = "<<&a << std::endl;s = 99;std::cout<<"s = "<<s <<" s's = "<<&s <<std::endl;}//fun2.cpp#include "head.h"void fun2(){std::cout<<"this is fun2:"<<std::endl;std::cout<<"a = "<<a <<" a's = "<<&a << std::endl;std::cout<<"s = "<<s <<" s's = "<<&s <<std::endl;}//main.cpp#include "head.h"int main(){fun1();fun2();return 0;}

执行结果:

this is fun1:a = 10 a's = 0x40b000s = 99 s's = 0x40a010this is fun2:a = 10 a's = 0x40b030s = 9 s's = 0x40a020

首先我们将该代码和上一节的代码比较发现,在头文件中定义一般的全局变量int 无法通过编译,而const int 和static int 都通过了编译,其中const int 通过编译是出乎我意料的,因为static 屏蔽其他文件对自己的变量访问,通过编译能够理解。事实上const int 通过编译的情况仅是在c++ 编译器下,在c编译器下,则是无法通过编译的。

尽管如此我们通过观察执行结果能够发现,fun1 和 fun2 中变量的无论是a 还是s 都不是同一变量,所以这种写法虽然能够通过编译,但会极大的浪费内存空间,而且执行结果可能还会与你的预期大相径庭。

总结:在头文件中定义const 和static 变量是能够通过编译的,但这样做会导致每一个include 该头文件的源文件都会定义一个该变量,造成内存的浪费。

4.正确声明和定义变量的方法–extern

函数声明和定义的分离十分简单,我们只需要在.h文件中写出函数返回值类型函数名、接受的参数,再到.cpp文件中去写函数的具体实现就行了。而变量的声明和定义的分离就要靠关键字extern了。extern 主要有两个作用,其中一个是对指定单元强制使用C 的编译器进行编译,另一个便是声明变量的作用范围的关键字,其声明的函数和变量可以在本编译单元或其他编译单元中使用。规范声明和定义变量的形式应该如下所示:

代码:

//head.h#pragma once#include <iostream>extern int a;extern const std::string str;void fun1();void fun2();//head.cpp#include "head.h"int a = 10;const std::string str = "hello world";//fun1.cpp#include "head.h"void fun1(){std::cout<<"this is fun1:"<<std::endl;std::cout<<"a = "<<a <<" a's address= "<<&a << std::endl;std::cout<<"str = "<<str<<" str's address= "<< &str<<std::endl;a = 99;}//fun2.cpp#include "head.h"void fun2(){std::cout<<"this is fun2:"<<std::endl;std::cout<<"a = "<<a <<" a's address= "<<&a << std::endl;std::cout<<"str = "<<str<<" str's address= "<< &str<<std::endl;}//main.cpp#include "head.h"int main(){fun1();fun2();return 0;}

执行结果:

this is fun1:a = 10 a's address= 0x40a010str = hello world str's address= 0x410060this is fun2:a = 99 a's address= 0x40a010str = hello world str's address= 0x410060

总结:当你需要定义一个变量给所有include 该头文件的单元共同使用时,你应该在.h文件中使用extern 关键字,表示该变量将在include这个头文件的编译单元中使用(即进行“声明”)。同时另外新建一个.cpp文件对该头文件中声明的变量进行统一定义(定义变量的文件实际上可以是任意include该头文件的.cpp文件,但为了避免混乱,我们通常约定是在和头文件同名的.cpp文件中进行定义,可以认为是一种规范)。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。