美文网首页
《C++ API设计》读书笔记之 《Pimpl惯用法》

《C++ API设计》读书笔记之 《Pimpl惯用法》

作者: hlp22 | 来源:发表于2017-11-25 18:31 被阅读0次

    Pimpl是“pointer to implementation”的缩写, 该技巧可以避免在头文件中暴露私有细节。Pimpl利用了c++的一个特点,即可以将类的数据成员定义为指向某个已经声明过的类型的指针, 这里的类型仅仅作为名字引入, 并没有被完整地定义。

    例如“自动定时器”的API, 它是一个具名对象,当对象被销毁时打印出其生存时间。

    • AutoTimer.h 如下:
    #ifdef _WIN32 
    #include <windows.h>
    #else
    #include <sys/time.h>
    #endif
    
    #include <string>
    
    class AutoTimer
    {
    public:
        explicit AutoTimer(const std::string name);
        ~AutoTimer();
    private:
        double getElapsed() const;
        std::string mName;
    
    #ifdef _WIN32 
        DWORD mStartTime;
    #else
        struct timeval mStartTime;
    #endif
        
    };
    

    从上面的h文件可知,该API暴露了定时器在不同平台上存储的底层细节,利用Pimpl特性, 可轻易的隐藏掉这些底层实现的细节, 利用Pimpl特效后新的h文件如下:

    #include <string>
    
    class AutoTimer{
    public:
        explicit AutoTimer(const std::string& name);
        ~AutoTimer();
    private:
        class Impl;
        Impl* mImpl;
    };
    

    新API更简洁, 没有任何平台相关的预处理指令, 他人也不能通过头文件了解类的任何私有成员。

    现在AutoTimer的功能实现都放在内嵌类Impl中了, 具体的AutoTimer的cpp文件如下:

    
    #include "AutoTimer.h"
    
    #ifdef _WIN32
    #include <windows.h>
    #else
    
    #include <sys/time.h>
    #include <iostream>
    
    #endif
    
    class AutoTimer::Impl
    {
    public:
        double getElapsed() const
        {
    #ifdef _WIN32
            return (GetTickCount() - mStartTime) / 1e3;
    #else
            struct timeval end_time;
            gettimeofday(&end_time, NULL);
            double t1 = mStartTime.tv_usec / 1e6 + mStartTime.tv_sec;
            double t2 = end_time.tv_usec / 1e6 + end_time.tv_sec;
            return t2 - t1;
    #endif
        }
    
        std::string mName;
    #ifdef _WIN32
        DWORD mStartTime;
    #else
        struct timeval mStartTime;
    #endif
    
    };
    
    AutoTimer::AutoTimer(const std::string &name)
    : mImpl(new AutoTimer::Impl())
    {
        mImpl->mName = name;
    #ifdef _WIN32
        mImpl->mStartTime = GetTickCount();
    #else
        gettimeofday(&mImpl->mStartTime, NULL);
    #endif
    }
    
    AutoTimer::~AutoTimer()
    {
        std::cout << mImpl->mName << ":took " << mImpl->getElapsed() << " secs" << std::endl;
        delete mImpl;
        mImpl = NULL;
    }
    

    将Impl类声明为AutoTimer类的私有内嵌类, 可以避免与该实现相关的符号污染全局命名空间。

    • Pimpl的优点:
    1. 信息隐藏, 将具体实现放在cpp文件中, h文件只暴露公有接口;
    2. 降低耦合;
    3. 加速编译;
    4. 更好的二进制兼容性,二进制兼容性指的是在升级库文件的时候(windows下的dll, uinx下的so),不必重新编译使用这个库的可执行文件或使用这个库的其他库文件,程序的功能不被破坏;
    5. 惰性分配;
    • 缺点
    1. 添加了一层封装, 可能进入性能问题;
    2. 代码可读性降低;

    相关文章

      网友评论

          本文标题:《C++ API设计》读书笔记之 《Pimpl惯用法》

          本文链接:https://www.haomeiwen.com/subject/eazqextx.html