精华内容
下载资源
问答
  • C++ .h和.cpp文件总结

    2013-05-03 10:30:57
    总结了.h文件能做什么,.cpp文件能做什么,总结了.h文件的本质,方便新手在遇到文件包含、类型声明、变量定义时能理清.h与.cpp关系,纯属个人总结文档。
  • C++——如何理解.h文件.cpp文件

    千次阅读 多人点赞 2017-03-21 17:09:42
    建立一个以类为名字的XX.h文件,声明类,再建立一个相同名字的XX.cpp文件(要#include “XX.h”),实现类中的函数, 在有main 函数的.cpp里声明函数,建一个与函数名一样的.cpp文件,实现该函数;并且要 #include ...

    这篇文章是之前摘的笔记放在电脑里,忘记在哪看到的了,就当是原创哈哈哈哈

    • 建立一个以类为名字的XX.h文件,声明类,再建立一个相同名字的XX.cpp文件(要#include “XX.h”),实现类中的函数, 在有main 函数的.cpp里声明函数,建一个与函数名一样的.cpp文件,实现该函数;并且要 #include “XX.h”

    一、源文件如何根据#include来关联头文件

    1. 系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。

      #include <xxx.h>

    2. 用户自定义的文件用双引号括起来,编译器首先会在用户目录下查找,然后在到C++安装目录(比如VC中可以指定和修改库文件查找路径,Unix和Linux中可以通过环境变量来设定)中查找,最后在系统文件中查找。

      #include “xxx.h”

    二、头文件如何来关联源文件

    • 这个问题实际上是说,已知头文件“a.h”声明了一系列数,“b.cpp”中实现了这些函数,那么如果我想在“c.cpp”中使用“a.h”中声明的这些在“b.cpp”中实现的函数,通常都是在“c.cpp”中使用#include“a.h”,那么c.cpp是怎样找到b.cpp中的实现呢?
    • 其实.cpp和.h文件名称没有任何直接关系,很多编译器都可以接受其他扩展名。比如偶现在看到偶们公司的源代码,.cpp文件由.cc文件替代了。
    • 在Turbo C中,采用命令行方式进行编译,命令行参数为文件的名称,默认的是.cpp和.h,但是也可以自定义为.xxx等等。
    • 谭浩强老师的《C程序设计》一书中提到,编译器预处理时,要对#include命令进行“文件包含处理”:将file2.c的全部内容复制到#include“file2.c”处。这也正说明了,为什么很多编译器并不care到底这个文件的后缀名是什么—-因为#include预处理就是完成了一个“复制并插入代码”的工作。
    • 编译的时候,并不会去找b.cpp文件中的函数实现,只有在link的时候才进行这个工作。我们在b.cpp或c.cpp中用#include“a.h”实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。
    • 在Unix下,甚至可以不在源文件中包括头文件,只需要在makefile中指名即可(不过这样大大降低了程序可读性,是个不好的习惯哦^_^)。在VC中,一帮情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。
    • 通常,编译器会在每个.o或.obj文件中都去找一下所需要的符号,而不是只在某个文件中找或者说找到一个就不找了。因此,如果在几个不同文件中实现了同一个函数,或者定义了同一个全局变量,链接的时候就会提示
      示“redefined”。

    声明与定义的区分:

    一个声明就是一个定义,除非 :
    声明:引入名称
    定义:引入实体
    1. 它声明了一个没有详细说明函数体的函数
    2. 它包含一个extern定义符且没有初始化函数或函数体
    3. 它是一个包含在一个类定义之内的静态类数据成员的声明
    4. 它必须在最终程序的某处准确的定义一次
    5. 它是一个类名声明,如class test;
    6. 它是一个typedef声明。

    言外之意

    1. 类的声明也就是定义
    2. 同时赋初值的声明也就是定义,如int a=2;
    3. 类非静态数据成员的声明也就是定义???
    4. 类的所有成员函数的声明也就是定义

    一个定义就是一个声明,除非:

    1. 它定义的是一个静态数据成员
    2. 它定义了一个非内联成员函数

    内部连接和外部连接:

    • 编译时每个文件会被编译成一个含有必要信息的源文件(又叫编译单元),然后编译单元会联结成一个和族文件同名的.o文件,.o文件把不同的编译单元中产生的符号联系起来,构成一个可执行文件。有两种截然不同的链接:内部的和外部的,将这些编译单元联系起来。

    内部连接:对这个定义的访问被局限在当前编译单元,其他编译单元无法访问。
    外部连接:可被其他单元访问,因此名称在整个执行文件中必须唯一。

    • 类的定义(同时也是声明),enum,struct,都是内部连接,内联函数,静态的非类成员数据也是typedef声明的类型也是内联结。
    • 非内联成员函数(包括静态成员)有外部连接,非内联函数,非静态自由函数(非类的成员函数)也是外连接。
      声明只对当前编译单元有用,他们不会影响到.o文件,
    • .h文件,由于该文件会被其他.cpp文件包含,但由于声明只是对当前编译单元有效,是不会将符号引入.o文件,所以该文件不能含有任何外部连接的符号(数据成员和函数)的定义。一般情况下也不要包含内连接符号的定义。

    综上所诉

    .h文件中能包含:

    1. 类成员数据的声明,但不能赋值
    2. 类静态数据成员的定义和赋值,但不建议,只是个声明就好。
    3. 类的成员函数的声明
    4. 非类成员函数的声明
    5. 常数的定义:如:const int a=5;
    6. 静态函数的定义
    7. 类的内联函数的定义

    不能包含:

    1. 所有非静态变量(不是类的数据成员)的声明
    2. 默认命名空间声明不要放在头文件,using namespace std;等应放在.cpp中,在.h文件中使用std::string
    展开全文
  • 理论上来说cpp文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体实现,任何一个cpp文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以cpp文件为单位的,...

    理论上来说cpp文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体实现,任何一个cpp文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以cpp文件为单位的,如果不在任何cpp文件中包含此头文件的话,这段代码就形同虚设),你可以在cpp文件中进行函数声明、变量声明、结构体声明,这也不成问题!!!

    那为何一定要分成头文件与cpp文件呢?

    又为何一般都在头件中进行函数、变量声明,宏声明,结构体声明呢?而在cpp文件中去进行变量定义,函数实现呢??

    原因如下

      1.如果在头文件中实现一个函数体,那么如果在多个cpp文件中引用它,而且又同时编译多个cpp文件,将其生成的目标文件连接成一个可执行文件,在每个引用此头文件的cpp文件所生成的目标文件中,都有一份这个函数的代码,如果这段函数又没有定义成局部函数,那么在连接时,就会发现多个相同的函数,就会报错,函数重复定义。

      2.如果在头文件中定义全局变量,势必会对此全局变量赋初值,那么在多个引用此头文件的cpp文件中同样存在相同变量名的拷贝,关键是此变量被赋了初值,所以编译器就会将此变量放入DATA段,最终在连接阶段,会在DATA段中存在多个相同的变量,它无法将这些变量统一成一个变量,统一变量的意思也就是仅为此变量分配一个空间,而不是多份空间。但是对于声明一个变量,这个变量在头文件没有赋初值,编译器就会将之放入 BSS段,连接器会对BSS段的多个同名变量仅分配一个存储空间。

      3.如果在cpp文件中声明宏、结构体、函数等,那么我要在另一个cpp文件中引用相应的宏、结构体、函数,就必须再做一次重复的工作(意思是说如果不去#include),如果我改了一个cpp文件中的一个声明,那么又忘了改其它cpp文件中的声明,这不就出了大问题了,程序的逻辑就变成了你不可想象的了,如果把这些公共的东东放在一个头文件中,想用它的cpp文件就只需要引用一个就OK了!!!这样岂不方便,要改某个声明的时候,只需要动一下头文件就行了。

      4.在头文件中声明结构体,函数等,当你需要将你的代码封装成一个库,让别人来用你的代码,你又不想公布源码,那么人家如何利用你的库呢?也就是如何利用你的库中的各个函数呢??一种方法是公布源码,别人想怎么用就怎么用,另一种是提供头文件,别人从头文件中看你的函数原型,这样人家才知道如何调用你写的函数,就如同你调用printf函数一样,里面的参数是怎样的?你是怎么知道的?还不是看人家的头文件中的相关声明啊。当然这些东东都成了C标准,就算不看人家的头文件,你一样可以知道怎么使用。

    c语言中cpp文件和头文件的困惑:

    本质上没有任何区别。 只不过一般:

    .h文件是头文件,内含函数声明、宏定义、结构体定义等内容。

    .cpp文件是程序文件,内含函数实现,变量定义等内容

    而且是什么后缀也没有关系,只不过编译器会默认对某些后缀的文件采取某些动作。你可以强制编译器把任何后缀的文件都当作cpp文件来编。

    这样分开写成两个文件是一个良好的编程风格。

    而且,比方说我在aaa.h里定义了一个函数的声明,然后我在aaa.h的同一个目录下建立aaa.c ,aaa.c里定义了这个函数的实现,然后在main函数所在.cpp文件里#include这个aaa.h 然后我就可以使用这个函数了。 main在运行时就会找到这个定义了这个函数的aaa.cpp文件。

    这是因为:

      首先:main函数为标准C/C++的程序入口,编译器会先找到main函数所在的文件

    假定编译程序编译myproj.c(其中含main())时,发现它include了mylib.h(其中声明了函数void test()),那么此时编译器将按照事先设定的路径(include路径列表及代码文件所在的路径)查找与之同名的实现文件(扩展名为.cpp或.c,此例中为mylib.c),如果找到该文件,并在其中找到该函数(此例中为void test())的实现代码,则继续编译;如果在指定目录找不到实现文件,或者在找到的实现文件及后续的各include文件中未找到实现代码,则返回一个编译错误。其实include的过程完全可以"看成"是一个文件拼接的过程,将声明和实现分别写在头文件及cpp文件中,或者将二者同时写在头文件中,理论上没有本质的区别。

    注意:之所以去查找与之同名的实现文件,是默认的Makefile文件设定的。当然也可以在Makefile中设定其他cpp文件,或者是

    以上是所谓动态方式。

    对于静态方式,基本所有的C/C++编译器都支持一种链接方式被称为Static Link,即所谓静态链接。

    在这种方式下,我们所要做的,就是写出包含函数、类等声明的头文件(a.h,b.h,...),以及他们对应的实现文件(a.cpp,b.cpp,...)。编译程序会将实现文件编译为静态的库文件(a.lib,b.lib,...)。在随后的代码重用过程中,我们只需要提供相应的头文件(.h)和相应的库文件(.lib),就可以使用过去的代码了。

    相对动态方式而言,静态方式的好处是实现代码的隐蔽性,即C++中提倡的"接口对外,实现代码不可见",有利于库文件的转发。

    复制代码
    提示:
    复制代码
    如果说难题最难的部分是基本概念,可能很多人都会持反对意见,但实际上也确实如此。我高中的时候学物理,老师抓的重点就是概念--概念一定要搞清,于是难题也成了容易题。如果你能分析清楚一道物理难题存在着几个物理过程,每一个过程都遵守那一条物理定律(比如动量守恒、牛II定律、能量守恒),那么就很轻松的根据定律列出这个过程的方程,N个过程必定是N个N元方程,难题也就迎刃而解。即便是高中的物理竞赛难题,最难之处也不过在于:
    
    (1)、混淆你的概念,让你无法分析出几个物理过程,或某个物理过程遵循的那条物理定律;
    
    (2)、存在高次方程,列出方程也解不出。而后者已经是数学的范畴了,所以说,最难之处还在于掌握清晰的概念;
    复制代码
    复制代码

    程序设计也是如此,如果概念很清晰,那基本上没什么难题(会难在数学上:比如算法的选择、时间空间与效率的取舍、稳定与资源的平衡上)。但是,要掌握清晰的概念也没那么容易。比如下面这个例子,看看你有没有很清晰透彻的认识。

    a.h文件中:

    void foo(); 

    a.cpp文件中:

    #include "a.h"
    void foo() 
    { 
        return; }

    我的问题出来了:#include "a.h"这句话是要还是不要?  

    main.cpp文件中:

    复制代码
    #include "a.h" 
    
    int main(int argc, char *argv[]) { foo(); return 0; }
    复制代码

    针对上面的代码,请回答三个问题:

    (1)a.c 中#include "a.h"这句话是不是多余的?(不一定)

    (2)为什么经常见 xx.c 里面 include 对应的 xx.h?

    (3)如果 a.c 中不写#include "a.h",那么编译器是不是会自动把 .h 文件里面的东西跟同名的 .c 文件绑定在一起?(不会)

    (请针对上面3道题仔细考虑10分钟,莫要着急看下面的解释。:) 考虑的越多,下面理解的就越深。)

    好了,时间到!请忘掉上面的3道题,以及对这三道题引发出的你的想法,然后再听我慢慢道来。

    正确的概念是:从C编译器角度看,.h和.c皆是浮云,就是改名为.txt、.doc也没有大的分别。换句话说,就是.h和.c没啥必然联系。.h中一般放的是同名.cpp文件中定义的变量、数组、函数的声明等需要让.c外部使用的声明。这个声明有啥用?只是让需要用这些声明的地方方便引用(因为使用前必须声明)。因为 #include "xx.h" 这个宏其实际意思就是把当前这一行删掉,把 xx.h 中的内容原封不动的插入在当前行的位置。由于想写这些函数声明的地方非常多(每一个调用 xx.c 中函数的地方,都要在使用前声明一下),所以用 #include "xx.h" 这个宏就简化了许多行代码--让预处理器自己替换好了。也就是说,xx.h 其实只是让需要写 xx.c 中函数声明的地方调用(可以少写几行字),至于 include 这个 .h 文件是谁,是 .h 还是 .c,还是与这个 .h 同名的 .c,都没有任何必然关系。

    这样你可能会说:啊?那我平时只想调用 xx.c 中的某个函数,却 include了 xx.h 文件,岂不是宏替换后出现了很多无用的声明?没错,确实引入了很多垃圾,但是它却省了你不少笔墨,并且整个版面也看起来清爽的多。鱼与熊掌不可得兼,就是这个道理。反正多些声明(.h一般只用来放声明,而放不定义,参见拙著"过马路,左右看")也无害处,又不会影响编译,何乐而不为呢?

    翻回头再看上面的3个问题,很好解答了吧?

    1.答:不一定。这个例子中显然是多余的。但是如果.c中的函数也需要调用同个.c中的其它函数,那么这个.c往往会include同名的.h,这样就不需要为声明和调用顺序而发愁了(C语言要求使用之前必须声明,而include同名.h一般会放在.c的开头)。有很多工程甚至把这种写法约定为代码规范,以规范出清晰的代码来。

    3.答:不会。问这个问题的人绝对是概念不清,要不就是想混水摸鱼。非常讨厌的是中国的很多考试出的都是这种烂题,生怕别人有个清楚的概念了,绝对要把考生搞晕。

    复制代码
    提示:
    搞清楚语法和概念说易也易,说难也难。窍门有三点: 不要晕着头工作,要抽空多思考思考,多看看书;
    
    看书要看好书,问人要问强人。烂书和烂人都会给你一个错误的概念,误导你;
    
    勤能补拙是良训,一分辛苦一分才;
    复制代码

    (1)通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。

    (2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

      头文件用来存放函数原型。

      头文件如何来关联源文件?

    这个问题实际上是说,已知头文件"a.h"声明了一系列函数(仅有函数原型,没有函数实现),"b.cpp"中实现了这些函数,那么如果我想在"c.cpp"中使用"a.h"中声明的这些在"b.cpp"中实现的函数,通常都是在"c.cpp"中使用#include "a.h",那么c.cpp是怎样找到b.cpp中的实现呢?

    其实.cpp和.h文件名称没有任何直接关系,很多编译器都可以接受其他扩展名。

    谭浩强老师的《C程序设计》一书中提到,编译器预处理时,要对#include命令进行"文件包含处理":将headfile.h的全部内容复制到#include "headfile.h"处。这也正说明了,为什么很多编译器并不care到底这个文件的后缀名是什么----因为#include预处理就是完成了一个"复制并插入代码"的工作。

      程序编译的时候,并不会去找b.cpp文件中的函数实现,只有在link的时候才进行这个工作。我们在b.cpp或c.cpp中用#include "a.h"实际上是引入相关声明,使得编译可以通过,程序并不关心实现是在哪里,是怎么实现的。源文件编译后成生了目标文件(.o或.obj文件),目标文件中,这些函数和变量就视作一个个符号。在link的时候,需要在makefile里面说明需要连接哪个.o或.obj文件(在这里是b.cpp生成的.o或.obj文件),此时,连接器会去这个.o或.obj文件中找在b.cpp中实现的函数,再把他们build到makefile中指定的那个可以执行文件中。

     (非常重要)

    在VC中,一般情况下不需要自己写makefile,只需要将需要的文件都包括在project中,VC会自动帮你把makefile写好。

    通常,连接器会在每个.o或.obj文件中都去找一下所需要的符号,而不是只在某个文件中找或者说找到一个就不找了。因此,如果在几个不同文件中实现了同一个函数,或者定义了同一个全局变量,链接的时候就会提示"redefined"。

    文章转载地址:http://www.cnblogs.com/stemon/p/4000266.html

    转载于:https://www.cnblogs.com/wangxue0218/p/4409707.html

    展开全文
  • .h文件.cpp文件组织结构

    千次阅读 2014-12-02 17:46:47
    .h文件一般包含在.cpp文件中,.h文件中多为变量类的声明,而.cpp文件才是变量类中函数的真正定义。 #include      哈,这个文件既不是 .c 也不是 .h ,那我们能不能不用它,改用 iostream.h ?一般 来说只是...

    1、包含关系:

    .h文件一般包含在.cpp文件中,.h文件中多为变量和类的声明,而.cpp文件才是变量和类中函数的真正定义。

    但是#include <iostream> 这个文件既不是.c也不是.h,那我们能不能不用它,改用iostream.h?一般来说只是为了使用cout这种对象是可以的。但意义上有差别,iostream和iostream.h是不一样的,一个是模板库,一个是C++库头文件。这里顺便提起一下以前的一件事,我刚进入上上家公司的时候,头要我们写个类模板,于是我就跟写一般的类一样把它分为两个文件,一个.h,一个.cpp,一个放定义,一个放实现。你们认为我的做法有问题么?写过模板的人都应该知道,这样是不行的,因为C++中的实现是针对类的实现,而不是针对模板的实现,分开写会导致连接时候找不到实现的。因此必须将模板的“定义”与“实现”写在同一个文件中,那这个文件究竟叫“.h”好呢还是“.cpp”好呢?都不好,它既不是类的定义也不是类的实现,它是模板,模板就是模板,那干脆就不用文件扩展名,STL就怎么干的,That’s OK!

    摘自:http://wenku.baidu.com/link?url=VxeYRH5Lf3cgBwCYO6vRnYIsRDr5UynV8WwFwlg5-39puJklkkf-UndQ40PDOpwl-_AHmmzXbGc6r231ZKMWQcTUjpVmUN2hPfCkddCTaii

    2、基类中有派生类的指针,或者A类中有B类指针,B类中有A类指针

    基类为CBase,子类有CDerivedA,CDerivedB,这倒没什么,但CBase中竟然有这种函数:

    class CBase
    {     
    	//…… 
    	virtual CDerivedA* GetA();
    	virtual CDerivedB* GetB();
    };

    DerivedA.h和DerivedB.h中需要include Base.h,而CBase竟然也用到了它的子类……那根据哪里用到就哪里包含的法则,Base.h是不是也要include DerivedA.h和DerivedB.h?这岂不是形成了“互相包含”?是的,如果出现了这种互相包含,VC++就会给出编译警告。当然如果你做了“反重复包含” 的工作,编译警告就不会出现,也不会出现“重复定义”,而取而代之的是“未定义”,程序还是通不过的。比如下面这个简单的例子:

    //基类 Base.h 
    #ifndef __BASE_H__#define __BASE_H__   
    #include"DerivedA.h"   
    class CBase
    { 
    public:     
    	CBase();    
    	~CBase();     
    	virtual CDerivedA*GetA(){return NULL;}
    };
    #endif
    //子类 DerivedA.h
    #ifndef __DERIVED_A_H__
    #define __DERIVED_A_H__
    #include"Base.h" 
    classCDerivedA:public CBase
    {
    public:
    	CDerivedA();
    	~CDerivedA();
    	virtual CDerivedA* GetA()
    	{return this;}
    }; 
    #endif

    编译出现的错误大致如下,但并非一定,甚至每次都有可能不同,这和编译的顺序有关系:

    error C2143:syntax error : missing ';' before '*' 

    error C2433:'CDerivedA' : 'virtual' not permitted on data declarations

    error C2501:'CDerivedA' : missing storage-class or type specifiers

    error C2501:'GetA' : missing storage-class or type specifiers   

    总之出现了这种基类“需要”子类的情况的话,就不能这样include了。取而代之的是使用一个类的声明:在Base.h中把“#include "DerivedA.h"”去掉,用“class CDerivedA;”取代它。这样编译就没有问题了。   

    OK OK,可能你又有问题了,如果基类中的函数不是“virtual CDerivedA* GetA()”,而是“virtual CDerivedA GetA()”,那怎么又通不过了?哇哈哈……老兄,你别扯了,我保证你找遍全世界的高手,也没有人能解决这个问题的,因为它逻辑上已经错误了,父在诞生的时候需要子,而父没诞生,哪来的子?又一个典型的鸡生蛋,蛋生鸡的问题。至于指针为什么就可以,因为指针在Win32中归根到底只是一个long型,它并不需要理解CDerivedA究竟何方神圣,只需要连接的时候找到就行了,反过来如果不是指针,CBase就要尝试寻找CDerivedA并生成实例,这可能吗?

    注意:在派生类从基类继承时,此时必须要有基类的完整声明,不能只有前向声明,即class Base;这样是不能通过编译的。如下代码:注意是错的

    #include<iostream>
    using namespace std;
    
    class A;
    class B:public A
    {
    	
    };
    
    class A
    {
    	int m;
    public:
    	void fun();
    };
    
    int main()
    {
    	A aa;
    	B bb;
    
    	system("pause");
    	return 0;
    }
    输出的错误提示为:error C2504: 'A' : base class undefined

    3、注意基类中有派生类时的构造函数

    //a.h
    #ifndef _AAA_
    #define _AAA_
    class Derived;
    class Base
    {
    protected:
    	Derived *p;
    public:
    	Base(){};
    	Base(Derived &d);
    	~Base(){}
    	void fun();
    };
    #endif
    
    //a.cpp
    #include"a.h"
    #include"b.h"
    Base::Base(Derived &d)
    {
    	p=&d;
    }
    void Base::fun()
    {
    	//可以调用Derived类中的函数,因为b.h中有函数声明
    	p->getA();
    }
    
    //b.h
    #ifndef _BBB_
    #define _BBB_
    #include"a.h"
    class Derived:public Base
    {
    	int a;
    	int b;
    public:
    	Derived(){}
    	Derived(int _a,int _b);
    	~Derived(){}
    	int getA();
    };
    #endif
    
    //b.cpp
    #include"b.h"
    #include<iostream>
    Derived::Derived(int _a,int _b)
    {
    	a=_a;
    	b=_b;
    	p=new Derived();
    	std::cout<<"111"<<std::endl;
    }
    
    int Derived::getA()
    {
    	std::cout<<a<<std::endl;
    	return a;
    }
    
    //test.cpp
    #include<iostream>
    #include"a.h"
    #include"b.h"
    using namespace std;
    
    int main()
    {
    	Base a;
    	Derived d(1,2);
    	Base b(d);
    	b.fun();
    
    	system("pause");
    	return 0;
    }
    
    如果基类只有一个构造函数,那么这个构造函数必须有缺省参数,而且只能复制为0,这样在构造派生类对象时,调用基类的构造函数,就不会存在再调用派生类构造函数的无限循环中了,即派生类中绝不能出现p=new Derived();因为这样将回到先有鸡还是先有蛋的问题上了。

    3、如果不用头文件,而直接定义基类和派生类又会有问题:如下是人和狗的问题,即Man类中有Dogs类的指针,表示人拥有的狗,而Dogs类中也有Man的指针,表示狗属于的人。这样如果先写Dogs类,就需要前向声明Man类,但是在Dogs类中的void printmsg();函数需要调用Man类中的char *getName()来得到名字,但是此时只有Man进行了前向声明,无法得知char *getName()函数的任何信息,因此就需要将void printmsg();函数放到char *getName()函数的声明或者定义之后。

    #include <iostream>
    using namespace std;
    
    class Man;
    class Dogs
    {
    public:
    	char name[20];
    	Man *master;
    	Dogs(char *name,Man *m):master(m)
    	{
    		strcpy(this->name,name);
    	}
    	Dogs(char *name)
    	{
    		strcpy(this->name,name);
    		master=NULL;
    	}
    	void addMasterMsg(Man &m)
    	{
    		master=&m;
    	}
    	//在这里不能定义,因为用到了Man类中的函数,而Man类在前面只是声明了类,
    	//并没有声明函数,因此不能用Man类中的函数
    	void printmsg();
    };
    
    
    class Man
    {
    	char name[20];
    public:
    	Dogs *dogs[10];
    	Man(char *name)
    	{
    		strcpy(this->name,name);
    		for(int i=0;i<10;i++)
    		{
    			dogs[i]=NULL;
    		}
    	}
    	void addDog(Dogs *dog,int i)
    	{
    		dogs[i-1]=dog;
    	}
    	void printmes()
    	{
    		cout<<name<<"的狗有:"<<endl;
    		for(int i=0;i<10;i++)
    		{
    			if(dogs[i])
    				cout<<"第"<<i<<"只狗名字是:"<<dogs[i]->name<<endl;
    			
    		}
    	}
    	char *getName()
    	{
    		return name;
    	}
    };
    
    void Dogs::printmsg()
    {
    	cout<<name<<"是"<<master->getName()<<"的狗"<<endl;
    }
    
    int main()
    {
    	Man m1("zhangsan");
    	cout<<m1.getName()<<endl;
    	Dogs d1=("dog1");
    	d1.addMasterMsg(m1);
    	m1.addDog(&d1,1);
    
    //	Dogs d2=("dog2",&m1);不知道为什么这样做构造函数不行,而且构造函数用初始化列表也不行。
    	//只能如上,用add函数增加
    //此处应该用Dogs d2=Dogs(“dog2”,&m1);才对,这样用的是默认的拷贝构造函数,临时构造一个对象,
    //完成拷贝,或者Dogs d2(“dog2”,&m1);也可以,千万不能用上面所示的错误的代码。赋值根本就不合//语法!!!
    	Dogs d3=Dogs("dog3",&m1);
    	Dogs d4("dog4",&m1);
    
    	m1.printmes();
    	d1.printmsg();
    
    	cout<<sizeof(Man)<<"	"<<sizeof(Dogs)<<endl;
    
    	system("pause");
    	return 0;
    }
    
    在这里如果有多个函数需要调用不同类之间的函数,就需要不断的进行声明,或者进行顺序化的定义,防止一个函数定义时,其调用的函数还没有声明。但是如果利用.h和.c文件来组织程序,就能够避免混乱的程序代码。此时,只需要分别定义Dogs.h实现Dogs类声明,当然要前向声明class Man,Man.h实现Man类声明,当然也要前向声明classDogs,或者包含Dogs.h文件。然后在定义Dogs.cpp和Man.cpp都包含Dogs.h和Man.h即可。完美的解决了上面的问题,而且思路很清晰,结构也很明了。

    4、注意externstatic关键字

    extern表示声明变量,一般用在.h文件中,而定义放在.cpp文件中,static关键字其实是为了屏蔽extern的声明,或者说是当前.cpp文件中的全局变量,但是不会被其他文件所用到。而且当前.cpp文件中有和extern变量重名,在此cpp中,用static变量。

    //a.h
    #ifndef _AAA_
    #define _AAA_
    extern int globalVariant;
    #endif
    
    //a.cpp
    #include"a.h"
    int globalVariant=10;
    
    //test.cpp
    #include<iostream>
    #include"a.h"
    using namespace std;
    
    static int globalVariant=100;
    
    int main()
    {
    	cout<<globalVariant<<endl;
    	globalVariant=3;
    	cout<<globalVariant<<endl;
    
    	system("pause");
    	return 0;
    }
    

    5、.cpp文件是需要编译的,但是.h文件是不需要进行编译的,如果包含.h文件就能够减少编译的时间。

    如果把函数的定义写在.h文件中,例如把fun1()写在了a.h中,而在b.cpp中包含了a.h,在test.cpp中也包含了a.h,那么此时就会报错,说函数重复定义了,因此把声明写在.h文件中,把定义写在.cpp文件中是比较好的办法。

    //a.h
    #ifndef _AAA_
    #define _AAA_
    int fun1()
    {
    	int a=1;
    	int b=2;
    	return a;
    }
    #endif
    
    //b.cpp
    #include"a.h"
    void fun2()
    {}
    
    //test.cpp
    #include<iostream>
    #include"a.h"
    using namespace std;
    
    int main()  
    {  
    	fun1();
    
        system("pause");  
        return 0;  
    }
    

    更多内容可看:C语言中关于.h和.c的问题

    展开全文
  • .h和.cpp文件的区别

    千次阅读 2019-06-22 03:35:50
    首先,我们可以将所有东西都放在一个.cpp文件内. 然后编译器就将这个.cpp编译成.obj,obj是什么东西? 就是编译单元了.一个程序,可以由一个编译单元组成, 也可以有多个编译单元组成. 如果你不想让你的源代码变得很难...

    关于头文件和源文件的分别

    首先,我们可以将所有东西都放在一个.cpp文件内.

    然后编译器就将这个.cpp编译成.obj,obj是什么东西?

    就是编译单元了.一个程序,可以由一个编译单元组成,

    也可以有多个编译单元组成. 如果你不想让你的源代码变得很难阅读的话,

    就请使用多个编译单元吧.(一个函数不能放到两个编译单元里面,但两个以上

    就可以分别放在一个单元,也就是cpp里面)

        那么就是一个.cpp对应一个.obj,然后将所有的obj链接起来(通过一个叫链接器的程序),

    组成一个.exe,也就是程序了.

        如果一个.cpp要用到另一个.cpp定义的函数怎么办? 只需在这个.cpp种写上他的函数声明

    就可以了.其余工作由链接器帮你完成,你可以随便调用该函数.

        链接器将所有的obj连接起来,但是如果碰巧有相同的函数或外部变量怎么办?他如何识别?

    一般来说是不能允许在同一个程序中,出现两个一样的函数名或外部变量名.

        但是只得庆幸的是,c++可以通过一种叫做链接属性的关键字来限定,你这个函数是属于整个程序

    公用的,还是只是在一个编译单元obj里面使用的.

        这些关键字就是extern 和 static; extern是外部链接的意思,也就是除了这个单元,外部的单元

    也是能够访问这个函数的.static 是内部链接,自属于自己单元.

    说了这么久,还没有说.h的作用呢?

        其实没有.h也能很好的工作,但是当你发现一个外部链接的函数或外部变量,需要许多份

    声明,因为c++这种语言,在使用函数和变量的时候,必须将他声明,为何要声明?声明之后才

    知道他的规格,才能更好的发现不和规格的部分.你别妄想一个编译单元,会自动从另一个

    编译单元那里得到什么信息,知道你是如何定义这个函数的.

        所以说,只要使用到该函数的单元,就必须写一份声明在那个.cpp里面,这样是不是很麻烦,

    而且,如果要修改,就必须一个一个修改.这真让人受不了.


    .h就是为了解决这个问题而诞生,他包含了这些公共的东西.然后所有需要使用该函数的.cpp,只需要

    用#include包含进去便可.以后需要修改,也只是修改一份内容.


    请注意不要滥用.h,.h里面不要写代码,.h不是.cpp的仓库,什么都塞到里面.

    如果在里面写代码,当其他.cpp包含他的时候,就会出现重复定义的情况,

    比如将函数func(){printf};放到头文件a.h,里面还有一些a.cpp需要的声明等;

    然后你发现b.cpp需要用到a.cpp里面的一个函数,就很高兴的将a.h包含进来.

    注意,#include并不是什么申请指令,他就是将指定的文件的内容,原封不动的拷贝

    进来.


    这时候实际上a.cpp和b.cpp都有一个func()函数的定义.

    如果这个函数是内部链接static的话,还好,浪费了一倍空间;

    如果是extern,外部链接(这个是默认情况),那么根据在同一个程序内不可出现

    同名函数的要求,连接器会毫不留情给你一个连接错误!

     

    转载于:https://www.cnblogs.com/shelvenn/archive/2008/02/02/1062446.html

    展开全文
  • C++中的 .h .cpp 详解

    千次阅读 2019-06-17 18:53:35
    通俗解释:.h和.cpp差不多就像书目录的关系吧,目录中对书中的章节内容进行简单表示,真正的实现是在书里面的。 一般的数据,数据结构,接口,还有类的定义放在.h文件中,可以叫他们头文件,可以#include 到别的...
  •   Windows编程课上,老师仔细讲了.h和.cpp文件。在将类定义类实现分开的时候,遇到的问题可真不少。   其中一个问题就是,A、B两个类,A的数据成员有B的对象,在a.cpp里我们要包含b.h,在mian里我们要包含a.h...
  • .h .cpp的区别

    千次阅读 2018-07-16 16:51:06
    .h和.cpp差不多就像书目录的关系吧,目录中对书中的章节内容进行简单表示,真正的实现是在书里面的。 一般的数据,数据结构,接口,还有类的定义放在.h文件中,可以叫他们头文件,可以#include 到别的文件中。...
  • c语言文件之间的关系
  • C++中的 .h .cpp 区别详解

    万次阅读 多人点赞 2018-09-29 09:15:02
    于是,在C++中就要分出了头(.h)文件和实现(.cpp)文件,并且也有了Package的概念。 对于以C起步,C#作为“母语”的我刚开始跟着导师学习C++对这方面还是感到很模糊。虽然我可以以C的知识面对C++的语法规范,用C#的...
  • C++中.h和.cpp关系

    千次阅读 2014-07-29 09:24:09
     首先,我们可以将所有东西都放在一个.cpp文件内, 然后编译器就将这个.cpp编译成.obj,即编译单元。 一个程序可以由一个编译单元组成,也可以由多个编译单元组成。 如果你不想让你的源代码变得很难阅读的话,就请...
  • 详解C 语言项目中.h文件和.c文件关系 在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个...
  • vs中.h文件与.cpp文件之间的切换

    千次阅读 2015-04-20 14:25:41
    拒绝繁琐!快捷键实现vs中.h文件与.cpp文件之间的切换
  • 简单来说,.h文件是头文件,用于函数的声明与定义,目的是让人清楚这个函数的功能以及其他一些信息相当于书中的目录。....hpp文件的实质就是将cpp文件中的代码混入h文件中,将函数的定义与实现写在一个文件中。 ...
  • 在阅读一个较大的解决方案中,对于其他文件夹下的.h和.cpp文件,有时候#include“XXX.h”文件,有时候是#include“XXX.cpp”文件,而且二者还不能更换。下面就好好分析一下他们二者的区别。 ...
  • 代码包包含一个完整的c++程序,从一个类的.h文件定义,到类的实体文件 .cpp,再到主文件main的构成 --- 基于翁恺的视频 04 自动售票机例子
  • C++中头文件(.h)和源文件(.cpp)分别有什么用

    千次阅读 多人点赞 2019-11-28 20:23:03
    这个问题在许多C++程序员看来会很搞笑,“这么简单的问题还用得着你废话!...下面就非常简洁明了地谈谈头文件(.h)和源文件(.cpp)应该怎么写。 头文件(.h): 写类的声明(包括类里面的成员方法的声明)、...
  • c++中xx.h和xx.cpp之间的联系

    千次阅读 2014-10-28 20:57:21
    不是很严格的讲,*.h文件做的是类的声明,包括类成员的定义函数的声明,而*.cpp文件做的类成员函数的具体实现(定义)。 一个*.h文件*.cpp文件一般是配对的。在*.cpp文件的第一行一般也是#include"*.h"文件,...
  • cpp文件为类的实现细节;主要注重的细节为: 在类外定义成员函数; 使用域作用符; 工程代码如下: //MyCircle.h # pragma once #include"MyPoint.h" class MyCircle { public : ...
  • 想把项目的全局参数都放在一个文件里,好方便对初始值修改。 步骤: 1.新建一个.h头文件,只声明参数为全局变量(注意是声明!!!)。 2.一定要extern,这才是声明全局变量 而且 一定不能赋初值 extern int a...
  • c语言中.h文件和.c文件关系

    千次阅读 多人点赞 2019-01-28 10:40:35
    详解C语言项目中.h文件和.c文件关系 在编译器只认识.c(.cpp))文件,而不知道.h是何物的年代,那时的人们写了很多的.c(.cpp)文件,渐渐地,人们发现在很多.c(.cpp)文件中的声明语句就是相同的,但他们却不得不一个字...
  • ROS添加CPP和h文件

    2020-10-18 09:00:12
    ROS添加CPP和h文件 indclude文件夹添加test.h #ifndef HELLO_ROS_PKG_TEST_H #define HELLO_ROS_PKG_TEST_H #include <ros/ros.h> class test { public: test(); void show() const; }; #endif //...
  • QT中main.cpp和mainwindow.cpp

    千次阅读 2018-09-27 14:49:02
    mainwindow.h mainwindow.cpp是主窗体的头文件和源文件,窗体的一些操作当然要在这里写了.   QtC++的关系,联系或者区别  QT是框架,C++是语言。 框架是为了让语言更加方便地开发 C++最著名的框架有...
  • 如何将Arduino的ino文件分解成多个.h和.cpp工程文件

    万次阅读 多人点赞 2019-01-07 17:35:11
    如何将Arduino的ino文件分解成多个.h和.cpp工程文件 当用Arduino做复杂工程项目时,程序难免会变得很大。这时候要修改个别参数或函数的时候会变得麻烦...不是所有的代码都方便分解成多个cpp文件进行管理的,特别是编...
  • .c文件和.h文件关系

    2018-02-06 23:05:41
    c语言项目中.h文件和.c文件关系 c语言中.h文件和.c文件的解析 编译器的工作过程 涉及到的函数、结构体、变量等比较多。通常,编写c/c++项目的方式是,有一个main.c文件,该文件的main函数作为接口,调用其他...
  • 前言 之前虽然学过C++,但是都是零零散散看的,很多特性,尤其是C++ 11的特性我都不知道。因此最近在看C++ primer,算是系统地过...在写书上的代码时,我又遇到了一个刚入门时反复遇到的问题:头文件(.h)和源文件(....
  • 简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段  2.词法与语法分析阶段 3.编译阶段,首先编译成纯汇编语句,再将...
  • C++大型项目中使用hpp和h文件代替cpp

    千次阅读 2019-07-07 14:59:16
    hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。 而实现代码将直接编译到调用者的obj文件中,...
  • C++中.h与.cpp关系

    千次阅读 2013-10-23 11:37:00
    C++中,一般把类的定义放到头文件(.h)中,把类的实现放到源文件(.cpp)中,所以,一般在C++中,一个类一般需要一个与类同名的头文件一个源文件。比如定个一个类 Sample,类中定个一个私有成员变量,两个公共成员...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,065
精华内容 24,026
关键字:

.h和.cpp文件的关系