博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对类前置声明和包含头文件的一点理解
阅读量:2428 次
发布时间:2019-05-10

本文共 3468 字,大约阅读时间需要 11 分钟。

 

作者:朱金灿

 

    类的前置声明(forward declaration)和包含头文件(#include)的区别常常会迷惑我们,特别是涉及两个类相互包含的时候。因此我们有必要搞清楚二者的区别以及二者的适用场合。

 

首先我们需要问一个问题是:为什么两个类不能互相包含头文件?所谓互相包含头文件,我举一个例子:我实现了两个类:图层类CLayer和符号类CSymbol,它们的大致关系是图层里包含有符号,符号里定义一个相关图层指针,具体请参考如下代码(注:以下代码仅供说明问题,不作为类设计参考,所以不适宜以此讨论类的设计,编译环境为Microsoft Visual C++ 2005,Windows XP + sp2,以下同):

 

      

 

     

   

现在开始编译,编译出错,出错信息如下:

1>正在编译...

1>TestUnix.cpp

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C2143: 语法错误: 缺少“;(在“*”的前面)

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

1>Layer.cpp

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C2143: 语法错误: 缺少“;(在“*”的前面)

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

1>f:/mytest/mytest/src/testunix/symbol.h(14) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

1>Symbol.cpp

1>f:/mytest/mytest/src/testunix/layer.h(18) : error C2143: 语法错误: 缺少“;(在“*”的前面)

1>f:/mytest/mytest/src/testunix/layer.h(18) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

1>f:/mytest/mytest/src/testunix/layer.h(18) : error C4430: 缺少类型说明符- 假定为int。注意: C++ 不支持默认int

 

    现在让我们分析一下编译出错信息(我发现分析编译信息对加深程序的编译过程的理解非常有好处)。首先我们明确:编译器在编译文件时,遇到#include   "x.h"时,就打开x.h文件进行编译,这相当于把x.h文件的内容放在#include   "x.h"处。编译信息告诉我们:它是先编译TestUnix.cpp文件的,那么接着它应该编译stdafx.h接着是Layer.h如果编译Layer.h那么会编译Symbol.h但是编译Symbol.h又应该编译Layer.h啊,这岂不是陷入一个死循环? 呵呵,如果没有预编译指令,是会这样的,实际上在编译Symbol.h再去编译Layer.hLayer.h头上的那个#pragma once就会告诉编译器:老兄,这个你已经编译过了,就不要再浪费力气编译了!那么编译器得到这个信息就会不再编译Layer.h而转回到编译Symbol.h的余下内容。当编译到CLayer *m_pRelLayer;这一行编译器就会迷惑了:CLayer是什么东西呢?我怎么没见过呢?那么它就得给出一条出错信息,告诉你CLayer没经定义就用了呢?TestUnix.cpp#include "Layer.h"这句算是宣告编译结束(呵呵,简单一句弯弯绕绕不断),下面轮到#include "Symbol.h",由于预编译指令的阻挡,Symbol.h实际上没有得到编译,接着再去编译TestUnix.cpp的余下内容。

 

     当然上面仅仅是我的一些推论,还没得到完全证实,不过我们可以稍微测试一下,假如在TestUnix.cpp#include "Layer.h"#include "Symbol.h"互换一下位置,那么会不会先提示CSymbol类没有定义呢?实际上是这样的。当然这个也不能完全证实我的推论。

 

    照这样看,两个类的互相包含头文件肯定出错,那么如何解决这种情况呢?一种办法是在A类中包含B类的头文件,在B类中前置盛明A类,不过注意的是B类使用A类变量必须通过指针来进行,具体见拙文:。为何不能前置声明只能通过指针来使用?通过分析这个实际上我们可以得出前置声明和包含头文件的区别。我们把CLayer类的代码改动一下,再看下面的代码:

    

 

   

然后编译,出现一个编译警告:>f:/mytest/mytest/src/testunix/layer.cpp(16) : warning C4150: 删除指向不完整“CSymbol”类型的指针;没有调用析构函数

1>                            f:/mytest/mytest/src/testunix/layer.h(9) : 参见“CSymbol”的声明

 

       看到这个警告,我想你一定悟到了什么。下面我说说我的结论:类的前置声明和包含头文件的区别在于类的前置声明是告诉编译器有这种类型,但是它没有告诉编译器这种类型的大小、成员函数和数据成员,而包含头文件则是完全告诉了编译器这种类型到底是怎样的(包括大小和成员)。这下我们也明白了为何前置声明只能使用指针来进行,因为指针大小在编译器是确定的。上面正因为前置声明不能提供析构函数信息,所以编译器提醒我们:CSymbol”类型的指针没有调用析构函数。如何解决这个问题呢?在Layer.cpp加上#include "Symbol.h"就可以消除这个警告。

 

 

 

转载地址:http://cfimb.baihongyu.com/

你可能感兴趣的文章
与吴恩达并肩战斗,她是 AI 界的女超人!|人物志
查看>>
微信手机 WeOS 的可行性到底有多大?
查看>>
阿里面试,我挂在了第四轮……
查看>>
C++ 程序员到高级架构师,必须经历的三个阶段
查看>>
和 Java、C# 等语言对比后,Python 简直酷上天了!
查看>>
程序媛到最后,拼的到底是什么?
查看>>
笑死!996 程序员竟然做了这个梦!| 每日趣闻
查看>>
“再见,微软!”
查看>>
ARM 发布新一代 CPU 和 GPU,实现 20% 性能提升!
查看>>
技术引路:机器学习仍大有可为,但方向在哪里?
查看>>
漫画:如何给女朋友解释什么是编译与反编译
查看>>
刷屏了!这篇 Python 学习贴,90% 的程序员都用的上!
查看>>
漫画:如何给女朋友解释什么是适配器模式?
查看>>
程序员又迎来一个好消息! | 每日趣闻
查看>>
Mac 被曝存在恶意漏洞:黑客可随意调动摄像头,波及四百万用户!
查看>>
拒绝与其他码农一致!CSDN定制T让你成为最靓的仔
查看>>
程序员情商低?看完这 4 类程序员我懂了!
查看>>
《长安十二时辰》里你不能不知道的 IT 技术 | 每日趣闻
查看>>
程序员爬取 3 万条评论,《长安十二时辰》槽点大揭秘!
查看>>
一年参加一次就够,全新升级的 AI 开发者大会议程出炉!
查看>>