• 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++。
    3�4�不需要�前置声明就可以了�其实3和4是一样的�引用在物理上也是一
    个指针�它的大小根据平台不同�可能是32位也可能是64位�反正我们不需要
    知道C的定义就可以确定这个成员变量的大小。
    5�不需要�有可能老式的编译器需要。标准库里面的容器像list� vector�
    map�
    在包括一个list<C>�vector<C>�map<C, C>类型的成员变量的时候�都不需要

    C的定义。因为它们内部其实也是使用C的指针作为成员变量�它们的大小一开
    始就是固定的了�不会根据模版参数的不同而改变。
    6�不需要�只要我们没有使用到C。
    7�需要�我们需要知道调用函数的签名。
    8�8的情况比较复杂�直接看代码会比较清楚一些。
     
                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和C�A
    是如何使用前置声明来取代直接包括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

  • 相关阅读:
    redis实现电商购物车
    redis应用于各种结构型和非结构型高热度数据访问加速
    Redis控制热点新闻的实效性
    Redis实现分表操作id唯一
    KrakenD网关V1.0.0文档初步翻译
    CentOS7环境下安装Chrome
    七夕秀恩爱新姿势!这波操作我给十分!
    小程序&#183;云开发实战
    小程序&#183;云开发实战
    小程序云开发实战
  • 原文地址:https://www.cnblogs.com/JeffreyCheung/p/5826072.html
Copyright © 2020-2023  润新知