编写高质量的C++代码--01 减少文件之间的编译依赖

一 概述:

        在实际来发过程中,经验略少的开发者经常遇到这样的问题:只对一个头文件中的定义进行了简单的修改,却导致很多文件的重新编译,浪费了大量的时间。这种使人厌烦的问题是由于那些文件依赖了(#include)你修改的头文件,当然,依赖是迟早的事,但是有什么好的解决办法减少没必要的编译动作呢?下面进行讲述。

最根本的出发点是尽量避免在.h文件中包含(#include)其他的.h文件,这样在对某一.h文件进行修改后,不会对另一个.h文件产生影响,从而避免多余的编译动作。比较抽象,举例说明:比如有两个.h文件,a.h和.h,还有一个.cpp文件c.pp,其依赖于b.h文件。如果b.h文件依赖于a.h文件,在对a.h进行修改后会导致b.h的#include内容发生变化,尽管其内容并未修改,但在编译过程中会使得c.cpp重新编译。


二 实现:

        下面讲述几种常用的解决方法:

        1 使用类的前向声明:

        基本思路是利用类的指针取代类的对象。因为不管何种类型,指针所占内存是固定大小的(不同平台下有所不同),基本形式如下:

//*******a.h
class CAddress
{
public:
    string m_strAddr;
};

//******b.h
#include “a.h”
class CStudent
{
private:
    string m_strName;
    char m_age;
    CAddress m_cAddress;
};

class CShool
{
private:
    string m_strName;
};

//******c.cpp
#include “b.h”
int main()
{
    CShool shool;

    return 0;
}

        代码如上所示,此时对a.h进行修改:

//*******a.h
class CAddress
{
public:
    string m_strAddr;
    int m_numDoor;
};

        尽管此时并未使用a.h中的类,再次编译时,也会导致c.cpp再次编译,而其与a.h无之间联系。问题就出在b.h中有 “#include a.h”

        下面我们通过”前向声明“对b.h进行修改,如下:

//******b.h
//! 前向声明
class CAddress;

class CStudent
{
private:
    string m_strName;
    char m_age;
    CAddress *m_pAddress;
};

class CShool
{
private:
    string m_strName;
};

        再修改a.h中CAddress的定义,不会导致c.cpp再次编译。
        如果有.cpp文件使用CStudent类,只需#include “a.h”即可。如下:

//*****d.cpp
#include “b.h”
#include “a.h”

void Test()
{
    CStudent student;
    //...
}
        当然,针对a.h的修改,d.cpp的重新编译是不可避免的。

        2.使用PImpl技术:

        pImpl,Private Implementation(or Pointer to Implementation)的缩写。是一种在类中只定义接口,而将私有数据成员封装在另一个实现类中的惯用法。该方法主要是为了隐藏类的数据以及减轻编译时的压力。
        在C++中,此种方法成为“柴郡猫”技术(其字面意思自行查阅)。它使用指向实现的指针来隐藏实现细节。即在主类定义中定义接口,私有成员封装在一个实现类中,主类使用一个形如”struct XxxxImpl* pimpl_”的不透明指针存储私有成员,解开类接口与实现的耦合,真正实现了接口和实现的分离。例如:

//*****test.h
//!PImpl前向声明
class CTestImp;
class CTest
{
public:
    CTest();
    ~CTest();
    void Public_Method();

private:
    //! PImpl指针
    CTestImp *pimpl_;
};

//*****test.cpp
class CTestImp
{
public:
    void Private_Method() {}
    int private_var_;
};

CTest::CTest() : pimpl_( new CTestImp() )
{
}

CTest::~CTest()
{
    if(pimpl_)
    {
        delete pimpl_;
    }
}

void CTest::Public_Method()
{
    pimpl_->Private_Method();
    pimpl_->private_var_ = 0;
}
        为了更好的封装性,将XxxxImpl类定义成一个local类,修改如下:

//*****test.h
class CTest
{
public:
    CTest();
    ~CTest();
    void Public_Method();

private:
    //!PImpl前向声明
    class CTestImp;
    
    //! PImpl指针
    CTestImp *pimpl_;
};

//*****test.cpp
class CTest::CTestImp
{
public:
    void Private_Method() {}
    int private_var_;
};

CTest::CTest() : pimpl_( new CTestImp() )
{
}

CTest::~CTest()
{
    if(pimpl_)
    {
        delete pimpl_;
    }
}

void CTest::Public_Method()
{
    pimpl_->Private_Method();
    pimpl_->private_var_ = 0;
}
        除了上述两种方法外,还可以使用基于接口的编程等方法,即提供接口供外部调用,具体实现在其子类中实现。这里用到的技术就是虚函数的多态性,具体细节在此不展开了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值