QT_FORWARD_DECLARE_CLASS
相当于class 类名。
那么他和#include 包含头文件有什么区别呢
首先我们为什么要包括头文件问题的回答很简单通常是我们需要获得某个
类型的定义(definition)。那么接下来的问题就是在什么情况下我们才需要类
型的定义在什么情况下我们只需要声明就足够了问题的回答是当我们需要知
道这个类型的大小或者需要知道它的函数签名的时候我们就需要获得它的定
义。
假设我们有类型A和类型C在哪些情况下在A需要C的定义
1.A继承至C
2.A有一个类型为C的成员变量
3.A有一个类型为C的指针的成员变量
4.A有一个类型为C的引用的成员变量
5.A有一个类型为std::list<C>的成员变量
6.A有一个函数它的签名中参数和返回值都是类型C
7.A有一个函数它的签名中参数和返回值都是类型C它调用了C的某个函数
代码在头文件中
8.A有一个函数它的签名中参数和返回值都是类型C(包括类型C本身C的引
用类型和C的指针类型)并且它会调用另外一个使用C的函数代码直接写在
A的头文件中
9.C和A在同一个名字空间里面
10.C和A在不同的名字空间里面
1没有任何办法必须要获得C的定义因为我们必须要知道C的成员变量
成员函数。
2需要C的定义因为我们要知道C的大小来确定A的大小但是可以使用Pimpl
惯用法来改善这一点详情请
看Hurb的Exceptional C++。
34不需要前置声明就可以了其实3和4是一样的引用在物理上也是一
个指针它的大小根据平台不同可能是32位也可能是64位反正我们不需要
知道C的定义就可以确定这个成员变量的大小。
5不需要有可能老式的编译器需要。标准库里面的容器像list vector
map
在包括一个list<C>vector<C>map<C, C>类型的成员变量的时候都不需要
C的定义。因为它们内部其实也是使用C的指针作为成员变量它们的大小一开
始就是固定的了不会根据模版参数的不同而改变。
6不需要只要我们没有使用到C。
7需要我们需要知道调用函数的签名。
88的情况比较复杂直接看代码会比较清楚一些。
C& doToC(C&);
C& doToC2(C& c) ...{return doToC(c);};
从上面的代码来看A的一个成员函数doToC2调用了另外一个成员函数doToC
但是无论是doToC2还是doToC它们的的参数和返回类型其实都是C的引用(换
成指针情况也一样)引用的赋值跟指针的赋值都是一样无非就是整形的赋
值所以这里即不需要知道C的大小也没有调用C的任何函数实际上这里并不
需要C的定义。
但是我们随便把其中一个C&换成C比如像下面的几种示例
1.
C& doToC(C&);
C& doToC2(C c) ...{return doToC(c);};
2.
C& doToC(C);
C& doToC2(C& c) {return doToC(c);};
3.
C doToC(C&);
C& doToC2(C& c) {return doToC(c);};
4.
C& doToC(C&);
C doToC2(C& c) {return doToC(c);};
无论哪一种其实都隐式包含了一个拷贝构造函数的调用比如1中参数c由拷
贝构造函数生成3中doToC的返回值是一个由拷贝构造函数生成的匿名对象。
因为我们调用了C的拷贝构造函数所以以上无论那种情形都需要知道C的定义。
9和10都一样我们都不需要知道C的定义只是10的情况下前置声明的语
法会稍微复杂一些。
最后给出一个完整的例子我们可以看到在两个不同名字空间的类型A和CA
是如何使用前置声明来取代直接包括C的头文件的
A.h
#pragma once
#include <list>
#include <vector>
#include <map>
#include <utility>
//不同名字空间的前置声明方式
namespace test1
...{
class C;
}
namespace test2
...{
//用using避免使用完全限定名
using test1::C;
class A
...{
public:
C useC(C);
C& doToC(C&);
C& doToC2(C& c) ...{return doToC(c);};
private:
std::list<C> _list;
std::vector<C> _vector;
std::map<C, C> _map;
C* _pc;
C& _rc;
};
}
C.h
#ifndef C_H
#define C_H
#include <iostream>
namespace test1
...{
class C
...{
public:
void print() ...{std::cout<<"Class C"<<std::endl;}
};
}
#endif // C_H
QT_FORWARD_DECLARE_CLASS的更多相关文章
- Qt全局宏和变量
1. Qt 全局宏定义 Qt版本号: QT_VERSION : (major << 16) + (minor << 8) + patch 检测版本号: QT_VERSION ...
随机推荐
- sublime text3开发python并设置快捷键
Package Control 安装方法 1.通过快捷键 ctrl+` 或者 View > Show Console 打开控制台,然后粘贴相应的 Python 安装代码: 2.Sublime T ...
- java线程锁
一.synchronized 这货可以锁对象,锁变量,锁方法,锁代码,好像什么都能锁,缺点就是如果一个锁堵了,其他的只能等待忙并不能把当前的锁给释放.二. ReentrantLockR ...
- Keil提示premature end of file错误 无法生成HEX文件
今天舍友在使用Keil UV4的时候遇到一个问题:Keil提示premature end of file,无法生成hex文件. 代码是没有错误的.那么问题就出在设置上面了. 百度了一圈,发现很少人解答 ...
- Android事件传递机制详解及最新源码分析——Activity篇
版权声明:本文出自汪磊的博客,转载请务必注明出处. 在前两篇我们共同探讨了事件传递机制<View篇>与<ViewGroup篇>,我们知道View触摸事件是ViewGroup传递 ...
- C++学习日记(二)————初始字符串类型
使用频率高,但操作复杂的数据有哪些? 做下总结: int; double;float;char;bool这些类型用的比较频繁,但并不复杂.但对于字符串来说(char数组)用的频繁但操作又复杂,只能用一 ...
- cocos2dx lua中异步加载网络图片,可用于显示微信头像
最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...
- NHibernate教程(5)--CRUD操作
NHibernate之旅(5):探索Insert, Update, Delete操作 2008-10-17 16:31 by 李永京, 42903 阅读, 73 评论, 收藏, 编辑 本节内容 操作 ...
- 通信原理课程设计Javaswing技术计算出PCM编码——猎八哥FLY
package keshe; import java.awt.BorderLayout; import java.awt.EventQueue; import javax.swing.JFrame; ...
- Squid代理服务器安装
代理服务器的功能是代替网络用户去访问网络信息,并把获得的信息返回给用户,其工作步骤大致如下: ) 客户机向代理服务器发起访问互联网的请求 ) 代理服务器收到请求后检查请求是否被允许,如果允许将会进行下 ...
- 团队作业7——Alpha冲刺之事后诸葛亮(宣告项目失败团队解散)
一.项目进度 1.4月5日,团队组建.满怀希望的能做好这个项目 2.4月12日,需求分析. 3.4月21日,需求改进,出现协作问题,没有做好. 4.做项目,学习新的知识,继续做项目,但是能力有限,团队 ...