C++的类声明、前置声明、定义及各自优势、使用场景

类前置声明可以减少编译时间,避免文件间的编译依赖,但可能导致编译问题,尤其是涉及模板、typedefs和默认参数时。当通过指针或引用使用类时可以使用前置声明,但在成员函数中使用指针成员时,需要在.cpp文件中包含完整定义。正确做法是在需要完整类型的地方引入头文件,以优化编译效率。
摘要由CSDN通过智能技术生成

当一个class的成员变量为另外一个类的指针类型的时候,此时,我们可以不在头文件中来包含这个类的头文件,即不使用include“.h”,而是采用前置声明的形式,对这个类进行一个类型的前置声明。
类的前置声明形式上:只有class关键字+类名,后面不带任何符号,即{}
白话解释:哎,b是个类,我要用它,你别管,它在某个地方是个真实的存在(也可能不存在,我就tm忽悠你的), 总之你不要管,我要用它”。这样跟编译器打过招呼之后,就能保证代码编译通过。
如下代码段

//文件a.h
 class B;
 class A
 {
 public:
 B *b;    
 };
 ​
 //文件b.h
 class B
 {
 public:
     void func1();
 };

a.h和b.h这两个文件中定义了两个类分别是A和B。其中A类有一个成员b是Class B的指针,这个时候可使用类前置声明

前置声明省去了#include的处理、降低文件之间的编译依赖,从而避免不必要的编译时间浪费。

利用前置声明的利弊

优点:避免#include, 避免修改一个被包含的头文件,导致整个文件重新编译。

缺点:(摘自Google)

如果前置声明关系到模板,typedefs, 默认参数和 using 声明,就很难决定它的具体样子了。

很难判断什么时候该用前置声明,什么时候该用 #include, 特别是涉及隐式转换运算符的时候。极端情况下,用前置声明代替 includes 甚至都会暗暗地改变代码的含义

前置声明函数或模板有时会害得头文件开发者难以轻易变动其 API. 就像扩大形参类型,加个自带默认参数的模板形参等等。

前置声明来自命名空间 std:: 的 symbol 时,其行为未定义。

仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员),后者会变慢且复杂起来。

还没有实践证实前置声明的优越性。

前置声明作用:
告诉编译器,这个类在其它地方定义,真正使用类的定义的时候才进行编译。
Note:类A在编译的时候不需要拿到类B的定义是因为这里面定义的是指针,而对于指针是不需要定义就可以进行内存布局的,在编译A的类的声明的时候,在进行内存布局的时候是不需要拿到B的定义的

但是在A的成员函数中如果使用这个指针b,则就需要在A对应的.cpp文件中引入B的定义。也就是在.cpp中需要include b.h

这里面对于前置声明,还需要注意一个事情,就是如果另外有一个类,这个类使用了类A,并且通过类A访问使用了A的成员变量b,比如下边这种情况

//文件c.h
 #include "a.h"
 class C
 {
 public:
     void print(){
         a.b->func1();
    }
    A a;
 };

编译时,讲会报错:不允许使用不完全(incomplete)类型A。
Because:C中访问了a成员b的成员函数。所以,此时也需要类B的完整定义,即include

比较正规的做法就是在C的头文件中也引入B的定义,这样编译器就能找到B的定义,从而编译通过
如法1

法1
//文件c.h
 #include "a.h"
 #include "b.h"
 class C
 {
    ...
 }
 法2
 //文件a.h
 #include "b.h"
 //class B;
 class A
 {
 public:
 B *b;    
 };

法2在类A的头文件中引入b.h头文件
这种写法,就是放弃了前置声明,直接在A中引入B的头文件,一次性将B的全部声明引入到A的头文件中。

这样也可以编译通过,但是会带来一个问题。就是以后所有引用A的头文件的其他文件,在编译的时候都需要编译一遍B,而在这些文件中可能压根就没有访问成员变量b。也就是说这些文件其实根本就不需要B的定义。这样会导致编译这些文件的时候,还需要编译一遍不相关的类B。这会导致编译缓慢

所以,这里面正确的做法是采用前一种方法。也就是说,头文件中能尽量少的引入头文件就应该尽量少的引用收文件,能用前置声明的就应该尽量使用前置声明

几种可以使用前置声明的 地方
**

以指针或引用的形式来引用类型

**

class A;
class B
{
    A *pa;//ok
    A &ra;//ok

    A * f(const A * pa);//ok
    A & f(const A & pa);//ok

};

友元

class A;
class B
{
    friend A; //ok
    friend class A;//ok

};

作为返回值或者参数

class A;
class B
{
    void f(A a);//ok

    A g();//ok
};

**

不可以使用前置声明的类型

**

使用完整的类引用

class A;
class B
{
    //A a; //error
    
};

被当作父类

class E;
class F: public E  //error VC6.0--'E': base class undefined  Qt5.6-invalid use of incomplete of 'class C'
{};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值