首页 > 编程语言 >C++ IMPL模式解析

C++ IMPL模式解析

时间:2022-12-05 08:56:43浏览次数:72  
标签:std string int C++ str 解析 IMPL 函数

https://blog.csdn.net/myw31415926/article/details/127722899

抛砖引玉

试想一个问题,如果有一套收发数据的网络接口,需要提供给其他同事或厂家使用,包含头文件和动态库,假设头文件如下:

// 版本1
class NetworkV1 {
public:
    int Send(const std::string str);
    int Recv(std::string &str);
private:
    int sockfd;
    char buf[1024];
};

用户直接 include 头文件,链接库文件即可。方法上没有问题,但问题是头文件中暴露的信息太多了,比如 private 成员变量,而且如果以后的版本中需要增加或删除某些变量,还需要通知用户修改头文件,太麻烦了。

为了解决这个问题,实现接口与实现分离,所以引入了 IMPL 模式。

C++ IMPL 模式

这里的 IMPL 其实就是 implement,即实现的意思,个人觉得 IMPL 严格上来讲,并不算一个设计模式,只是一个更好的 隐藏实现的方法 。已 C++ 为例,它不仅仅是将类的声明和实现放在不同的文件中,更重要的是隐藏细节,只暴露用户必须的接口部分。先看一版改进的代码:

// network.h
// 版本2
class NetworkV2 {
public:
    int Send(const std::string str);
    int Recv(std::string &str);

private:
    struct Impl;
    std::shared_ptr<Impl> impl;
};


// network.cpp
// 版本2
struct NetworkV2::Impl {
    int sockfd;
    char buf[1024];
};

int NetworkV2::Send(const std::string str) {
    // TODO ...
    // send(impl->sockfd, str.c_str(), str.size(), 0);
    return str.size();
}
int NetworkV2::Recv(std::string &str) {
    // TODO ...
    // recv(impl->sockfd, impl->buf, 1024, 0);
    return str.size();
}

这样就做到了隐藏类中的成员变量了,核心思想就是 将成员变量打包放在一个结构体中 ,无论以后的版本中有无删减成员变量,都不会对头文件造成任何影响,这是目前 C++ IMPL 中非常常见的一种调用方法。类似于 C 语言中的 void* 指针,可以在需要的时候转换成任意对象。

完全隐藏成员变量

但是上一种模式还是会有 private 的成员变量,如果是想要完全隐藏,只保留接口呢?我在学习上交所 CTP 接口的时候,还看见过一种新的方法,核心思想是 使用虚函数和继承 。这种模式头文件中只保留接口,不会有任何的成员变量,代码如下:

// network.h
// 版本3
class NetworkV3 {
public:
    virtual int Send(const std::string str) = 0;
    virtual int Recv(std::string &str) = 0;

    // 创建和销毁函数
    static NetworkV3* New();
    static void Delete(NetworkV3 *net);
};

// network.cpp
// 版本3
class NetworkV3Impl final : public NetworkV3 {
public:
    int Send(const std::string str) override {
        std::cout << "NetworkV3Impl::Send: " << str << std::endl;
        return str.size();
    }

    int Recv(std::string &str) {
        str = "ok";
        std::cout << "NetworkV3Impl::Recv: " << str << std::endl;
        return str.size();
    }
};

// 创建和销毁函数
NetworkV3* NetworkV3::New() {
    return (new NetworkV3Impl());
}
void NetworkV3::Delete(NetworkV3 *net) {
    delete (NetworkV3Impl*)net;
}

虽然消除了 private 成员变量,但增加了两个静态成员函数 New 和 Delete ,用于创建和销毁对象,也不需要用户自己管理内存,使用上很方便,像 CTP 的 C++ 接口就是采用的这种模式。但后来查资料,发现这种方法有两个主要的弊端:
虚函数开销:虚函数需要使用虚函数表指针间接调用,运行时才能确定调用哪一个函数,无法在编译期间内联优化。在上一版中,在编译期就能确定调用哪一个函数,根本用不到虚函数的特性。
二进制兼容:虚函数是按照索引查询虚函数表来调用的,新增或调整虚函数顺序会造成索引变化,导致新接口在二进制层面不能兼容老接口,就是在末尾增加虚函数,也会有风险。

标签:std,string,int,C++,str,解析,IMPL,函数
From: https://www.cnblogs.com/sggggr/p/16951398.html

相关文章

  • c++ - 误删cmake-build-debug ,clion具体解决
    1.背景误删了cmake-build-debug文件夹或者破会了内部文件2.解决如果cmake-build-debug文件夹删除了,需要在同一个文件添加这个文件夹如果cmake-build-debug文件夹还存在......
  • C++中STL容器详解
    STL是提高C++编写效率的一个利器。——闫学灿一.string参考文章C语言中文网文章:C++string详解,C++字符串详解介绍C语言中,字符串是以\0结尾的一些字符的集合,为了操......
  • c++ thread
     示例:voidtest(inttimes){//子线程睡眠times秒this_thread是当前子线程this_thread::sleep_for(chrono::seconds(times));std::cout<<"hellow......
  • 在C#中使用Irony实现SQL语句的解析
    https://sunnycoding.cn/2019/07/11/sql-parser-with-irony-in-csharp/ 本文结构定义语法语法测试语句解析获得解析结果总结在上一篇博文中,我介绍了LOGO语言的C......
  • C++
    通讯录管理系统1、系统需求通讯录是一个可以记录亲人、好友信息的工具。本教程主要利用C++来实现一个通讯录管理系统系统中需要实现的功能如下:添加联系人:向通讯录中......
  • c++中的类 - 类继承
    1,派生类继承了基类的所有成员函数和数据成员(构造函数、析构函数和操作符重载函数外)。2,当不指明继承方式时,默认为私有继承。3,基类的私有成员仅在基类中可见,在派生类中是不......
  • VSCode配置C-C++环境
    转载自:【教程】VScode中配置C语言/C++运行环境_哔哩哔哩_bilibili下载编辑器VScode官网:https://code.visualstudio.com/安装VScode(建议附加任务全部勾选)下载......
  • 1.C++入门基础(上)
    C++关键字C++关键字全集(参考C++Primer):asmautobad_castbad_typeidboolbreakcasecatchcharclassconstconst_castcontinuedefaultdelete......
  • 3.C++和C的混合编译
    简介C++语言的创建初衷是"abetterC",但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++......
  • 2.C++入门基础(下)
    内联函数C++中函数的使用我们已经比较清楚了,与C语言中函数的使用大多相同,主要是增加了重载的特性,对C语言的函数的一些缺陷做了一些补充。那么对于一些比较简单却又经常使......