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、注意extern和static关键字

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文件组织结构相关推荐

  1. Qt 生成 ui 对应的 h 文件和 cpp 文件的方法

    2018-04-04 创建人:Ruo_Xiao 开发环境:Qt 5.2.1 邮箱:xclsoftware@163.com 将ui文件copy到uic.exe同一目录下(qt的安装目录中bin下). 打 ...

  2. c语言中.h文件和.cpp文件解析

    理论上来说cpp文件与头文件里的内容,只要是C语言所支持的,无论写什么都可以的,比如你在头文件中写函数体实现,任何一个cpp文件包含此头文件就可以将这个函数编译成目标文件的一部分(编译是以cpp文件为 ...

  3. Ardunino——面向对象语言学习篇5——.h文件和.cpp文件(制作Arduino类库)

    学习自:太极创客. 实践是检验真理的唯一标准,通过arduino进行实践学习C++或许是一个不错的选择. 前篇回顾   前篇学习了构造函数与析构函数,总体来说就是对象创建和消失时候要执行得函数. 前提 ...

  4. C程序保存为.c文件和.cpp文件的区别

    在学习数据结构时,看到老师把C语言和C++混用,并将文件保存为.cpp类型.关于.c文件和.cpp文件的区别,以下是我的一点看法: 1.首先应该明白,C++是在C语言的基础上拓展而来的,因此C++的语 ...

  5. C++——如何理解.h文件和.cpp文件

    这篇文章是之前摘的笔记放在电脑里,忘记在哪看到的了,就当是原创哈哈哈哈 建立一个以类为名字的XX.h文件,声明类,再建立一个相同名字的XX.cpp文件(要#include "XX.h&quo ...

  6. .c文件与.cpp文件区别

    在编译源文件时,C编译器和C++编译器都会对符号(函数或变量)名作某些修正,但两者采用的修正方法不同,所以两者生成的目标文件不能互相链接.      在C++中使用extern "C&quo ...

  7. Markup.h 和 Markup.cpp 文件(下一篇)内容,直接复制创建就行了

    Markup.h文件 // Markup.h: interface for the CMarkup class. // // Markup Release 11.5 // Copyright (C) ...

  8. C++ .h(头文件) 与 .cpp(源文件) 的使用

    .h 文件: .h是头文件 ,里面主要是写类的声明(包括类里面的成员和方法的声明).函数原型.#define常数等, 注意.h文件写的时候有特定的格式就是开头和结尾 #ifndef TEST_HEAD ...

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

    原文出处:https://www.cnblogs.com/fenghuan/p/4794514.html 头文件(.h): 写类的声明(包括类里面的成员和方法的声明).函数原型.#define常数等, ...

最新文章

  1. 小公司如何提高效率?
  2. “面试不败计划”:集合、日期、异常、序列化、jvm、其他
  3. python3使用serial以及pyserial包读取串口数据并解析字节数组,涉及数据移位以及Python无符号整数转为有符号整数操作
  4. JavaScript实现RadixSort基数排序算法(附完整源码)
  5. datasnap 2011连接池,数据集对象池
  6. 一个顾客买了价值x元的商品(C语言)
  7. python 用if判断一个数是不是整数_Python基础教程07-函数和模块的使用
  8. s:radio 赋值取值和添加事件
  9. VirtuoZo数字摄影测量(二)——模型定向与核线影像生成
  10. IDEA使用技巧--在文件导航栏中屏蔽指定后缀名的文件
  11. openwrt源码下载移植
  12. 计算机网络 第四章网络层笔记
  13. CentOS7.6新增或修改SSH端口号的步骤
  14. 【控制理论】矩阵求逆引理推导及理解
  15. SQL 的各种 join 用法
  16. Sails基础之Models层的config/datastores配置
  17. linux网卡配置没生效,linux上网络配置不生效的解决办法
  18. Gradle下载与安装
  19. 赵小楼《天道》深度解析(75)客观是对现有事实的认可,嘴上认可可不行,得心里认,否则就是自欺
  20. 金属材料领域模型与通用语言之钢铁加工——面向软件领域的简介

热门文章

  1. 探秘Linux特殊设备文件:(/dev/null,/dev/zero,/dev/random,/dev/urandom等)
  2. iOS拍摄视频,自定义拍摄界面,高清压缩,添加水印
  3. 两少年玩防狼喷雾剂 致广州地铁发生踩踏
  4. 一个数如果恰好等于它的因子之和,这个数就称为完数。例如6=1+2+3.编程 找出1000以内的所有完数
  5. 《铁道科学与工程学报》投稿经验
  6. Redis之父Antirez谈副业与主业
  7. 服务器系统fluent,用云服务器算fluent
  8. 学数答题160904-不等式
  9. 3月9日——3月13日一年级课程表
  10. 对麦克斯韦方程组的理解(非常详细)