• C++ 编译期封装-Pimpl技术


    Pimpl技术——编译期封装

    Pimpl 意思为“具体实现的指针”(Pointer to Implementation),

    它通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏,

    是隐藏实现,降低耦合性和分离接口实现的一个现代 C++ 技术,并有着“编译防火墙(compilation firewall)”的名头。

    Pimpl技术的基本应用

    其中利用了C++11的std::unique_ptr来让Impl指针的内存更易受控制。

    此外由于声明了析构函数,导致默认的移动构造/赋值函数不能生成,若默认行为符合自己的需求,则需显式声明 = default

    (当只在.h里,Impl是个不完整的类型,所以无法在.h类直接 = default,而是在.h声明,在.cpp使= default)

    若需要给类提供拷贝性质的函数,需要额外花点心思处理std::unique_ptr(该智能指针不支持拷贝)。

    // my_class.h
    #pragma once
    #include <memory>
    
    class my_class {
        //  ... 所有的公有/保护接口都可以放在这里 ...
        my_class();
        ~my_class();
        my_class(my_class&& v);    //移动构造
        my_class& operator=(my_class&& v);    //移动赋值
    private:
        class Impl; 
        std::unique_ptr<Impl> pimpl;
    };
    // my_class.cpp
    // ...include其它要依赖的头文件...
    #include "my_class.h"
    
    class my_class::Impl {
      // 在这里定义所有私有变量和方法(换句话说是my_class类的具体实现细节内容)
      // 现在可以改变实现,而依赖my_class.h的其他类无需重新编译...
    };
    
    my_class::my_class():pimpl(std::make_unique<Impl>()){
        // ...初始化pimpl... 
    }
    my_class::~my_class() = default;
    my_class::my_class(my_class&& v) = default;
    my_class::my_class& operator=(my_class&& v) = default;

    代码示例

    //View.h文件
    #pragma once
    #include <memory>
    
    class View
    {
    public:
        View();
        ~View();
        View(View&& v);
        View& operator=(View&& v);
        void display();
    private:
        class Impl;
        std::unique_ptr<Impl> pimpl; 
    };
    //View.cpp文件
    #include <iostream>
    #include <string>
    #include "View.h"
    
    /////////////////////////////////////////////////////////
    //下面是View::Impl的定义,也就是体现了View类的具体实现细节
    
    class View::Impl {
        std::string name;
    public:
        Impl();
        void printName();
    };
    
    View::Impl::Impl(){
        name = "DefaultName";
    }
    
    void View::Impl::printName(){
        std::cout << "this is my name:" << name;
    }
    
    ///////////////////////////////////////////////////////////
    //下面是View类接口的实现
    
    View::View():pimpl(std::make_unique<Impl>()){
    }
    
    View::~View() = default;
    View::View(View&& v) = default;
    View& View::operator = (View&& v) = default;
    
    void View::display(){
        pimpl->printName();
    }

    什么时候使用Pimpl技术?

    可以看到Pimpl拥有如下优点:

    • 减少依赖项(降低耦合性):其一减少原类不必要的头文件的依赖,加速编译;其二对Impl类进行修改,无需重新编译原类。

    • 接口和实现的分离(隐藏了类的实现)私有成员完全可以隐藏在共有接口之外,给用户一个间接明了的使用接口,尤其适合闭源API设计。

    • 可使用惰性分配技术:类的某部分实现可以写成按需分配或者实际使用时再分配,从而节省资源。

    Pimpl也拥有一些缺点:

    • 每个类需要占用小小额外的指针内存。

    • 每个类每次访问具体实现时都要多一个间接指针操作的开销,并且再使用、阅读和调试上都可能有所不便。

    可以说,在性能/内存要求不敏感(非极端底层)的领域,Pimpl技术可以有相当不错的发挥和作用。

  • 相关阅读:
    实验 6:OpenDaylight 实验——OpenDaylight 及 Postman 实现流表下发
    实验 5:OpenFlow 协议分析和 OpenDaylight 安装
    实验 4:Open vSwitch 实验——Mininet 中使用 OVS 命令
    实验 3:Mininet 实验——测量路径的损耗率
    软件工程第一次作业
    实验 2:Mininet 实验——拓扑的命令脚本生成
    实验 1:Mininet 源码安装和可视化拓扑工具
    软件工程第一次作业
    软工实践个人总结
    2020软工第二次结对作业
  • 原文地址:https://www.cnblogs.com/KillerAery/p/9539705.html
Copyright © 2020-2023  润新知