一 简介

今天阅读《C++ API设计》一书,设计到了PIMPL IDIOM,于是研究了一下。

PIMPL IDIOM是由JeffSumner首次提出的概念,意为“pointertoimplementation”,即指向实现的指针。这是一种用来隐藏源代码头文件实现细节的方法,可以保持API接口与实现的高度分离。严格来说PIMPL并不是一种设计模式,而只是一个术语,可以当作桥接模式的一个特例。

二 使用PIMPL

PIMPL基于这样一个事实:在C++类中,允许定义一个成员指针,指向一个已声明过的类型。在头文件中只是存放该类型的声明,而具体的定义是存放在CPP文件中,这样就可以隐藏类型的具体实现。

下图截取自《C++API设计》:


下面的例子展示了PIMPL的用法。AutoTimer是一个C++类,其作用是在对象被销毁时,打印出该对想的生存时间:

//autotimer.h

#ifdef_WIN32

#include<windows.h>

#else

#include<sys/time.h>

#endif

#include<string>

classAutoTimer

{

public:

///Create a new timer object with a human-readable name

explicitAutoTimer(const std::string &name);

///On destruction, the timer reports how long it was alive

~AutoTimer();

private:

//Return how long the object has been alive

doubleGetElapsed() const;

std::stringmName;

#ifdef_WIN32

DWORDmStartTime;

#else

structtimeval mStartTime;

#endif

};


很明显,这个类设计的并不好,它违反了很多优秀API设计的准则。首先,它包含了平台相关的定义;其次,它暴露了底层的具体实现,不同平台是如何存储的具体细节。


我们的目的很明确,就是隐藏具体实现细节,只将公开接口暴露出来。此时PIMPL闪亮登场,我们可以将具体细节放在PIMPL指向的类型中,将该类型的定义放在CPP文件中,用户将不会看到CPP文件。重构后的头文件代码如下:

//autotimer.h

#include<string>

classAutoTimer

{

public:

explicitAutoTimer(const std::string &name);

~AutoTimer();

private:

classImpl;//嵌套类

Impl*mImpl;//PIMPL指针

};



可以看出,我们声明了一个Impl类型,并定义了一个指向该类型的指针mImpl;但该类型的定义我们并不能看到,因为其定义放在CPP文件中了。接下来我们看CPP文件:

//autotimer.cpp

#include"autotimer.h"

#include<iostream>

#if_WIN32

#include<windows.h>

#else

#include<sys/time.h>

#endif

classAutoTimer::Impl

{

public:

doubleGetElapsed() const

{

#ifdef_WIN32

return(GetTickCount() - mStartTime) / 1e3;

#else

structtimeval end_time;

gettimeofday(&end_time,NULL);

doublet1 1⁄4 mStartTime.tv_usec / 1e6 þ mStartTime.tv_sec;

doublet2 1⁄4 end_time.tv_usec / 1e6 þ end_time.tv_sec;

returnt2 - t1;

#endif

}

std::stringmName;

#ifdef_WIN32

DWORDmStartTime;

#else

structtimeval mStartTime;

#endif

};

AutoTimer::AutoTimer(conststd::string &name) :

mImpl(newAutoTimer::Impl())

{

mImpl->mName1⁄4 name;

#ifdef_WIN32

mImpl->mStartTime1⁄4 GetTickCount();

#else

gettimeofday(&mImpl->mStartTime,NULL);

#endif

}

AutoTimer::AutoTimer()

{

std::cout<< mImpl->mName << ": took " <<mImpl->GetElapsed()

<<" secs" << std::endl;

deletemImpl;

mImpl= NULL;

}

CPP文件中,我们但到了Impl类的具体定义,其包含了平台相关的代码,以及底层如何存储的细节。


在头文件中,我们声明Impl类为一个私有的嵌套类型,这是有好处的。声明为嵌套类不会污染全局的命名空间,而私有类型确保只有AutoTimer类的成员能够使用该类型,其他类无法访问。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐