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

C++ IMPL模式解析(下)

时间:2022-12-05 11:34:45浏览次数:63  
标签:std NetworkV4Impl 二进制 NetworkV4 C++ 兼容 str 解析 IMPL

二进制兼容

在上一章结尾处提到了二进制兼容的概念,这里先说说二进制兼容的问题。

为什么是二进制兼容

简单说,就是我的可执行程序调用你的动态库(so/dll),若动态库发生改动,我替换库文件后仍可以直接运行,这就是二进制兼容。若需要重新编译才能运行,就是二进制兼容。

为什么会二进制兼容

二进制不兼容的根本原因是升级动态库导致内存模型发生了改变。如原本类中有一个int属性a,升级之后在a前面添加了一个int属性b。升级后,类多占用了4个四节。如果调用者不重新编译,原来调用a的地方已经变成b了,自然就会出问题。增加接口也会造成这样的问题,此时必须重新编译链接可执行程序,以更新动态库在程序中的对象模型。

什么情况会导致二进制不兼容

简单来说,只要改变了对象的内存模型,就会导致二进制不兼容。例如:
增加虚函数或改变虚函数的声明顺序,这会导致虚函数表中的函数位置发生变化(在末尾增加是一种取巧行为,如果类被继承,也还是会出问题);
增加非静态属性;
调整非静态属性的声明顺序
例如,对于第三个版本,略做一点修改:

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);
};

这里,我将 Send 和 Recv 的声明顺序颠倒一下,若不重新编译,调用时就会出问题。我是在 CentOS 上使用 g++ 9.3 编译了,此时虽然没有发生崩溃,但明显有调用错误,即原来调用 Send 的地方改为调用 Recv 了,若参数不一致,则直接没有调用。只有重新编译后,程序才能正常运行。

隐藏子类---解决二进制兼容

可以通过一个隐藏的子类和友元的方式来解决二进制兼容的问题,其实就是 上一章 中两个版本(版本二和版本三)的结合。具体代码如下:

// network.h
// 版本4
class NetworkV4 {
public:
    int Send(const std::string str);
    int Recv(std::string &str);
    
    // 创建和销毁函数
    static NetworkV4* New();
    static void Delete(NetworkV4 *net);

protected:
    NetworkV4();
    ~NetworkV4();
};


// network.cpp
// 版本4
class NetworkV4Impl : public NetworkV4
{
    // 友元,NetworkV4 中可以访问 NetworkV4Impl 的私有成员
    friend class NetworkV4;

private:
    // NetworkV4 的成员变量
    std::string str_;
};

NetworkV4::NetworkV4() {}
NetworkV4::~NetworkV4() {}

int NetworkV4::Send(const std::string str) {
    NetworkV4Impl *impl = (NetworkV4Impl*)this;

    // TODO: 通过访问 impl 的成员变量,实现 NetworkV4::Send
    impl->str_ = str;
    std::cout << "NetworkV4::Send: " << impl->str_ << std::endl;
    return impl->str_.size();
}

int NetworkV4::Recv(std::string &str) {
    NetworkV4Impl *impl = (NetworkV4Impl*)this;

    // TODO: 通过访问 impl 的成员变量,实现 NetworkV4::Recv
    impl->str_ = "ok";
    str = impl->str_;
    std::cout << "NetworkV4::Recv: " << str << std::endl;
    return str.size();
}

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

NetworkV4的接口只有成员函数(非虚函数),无成员变量,但构造函数和析构函数必须定义为protected,防止外部创建对象,要使用静态方法创建对象。

只是有一点看着比较奇怪,为啥 NetworkV4Impl 一定要继承 NetworkV4 ,不继承是否可以。很显然的可以的,但如果不继承,就需要定义一个 NetworkV4Impl 全局变量,用于在 NetworkV4 中调用,而这与 NetworkV2 的方法几乎是一致的(NetworkV2 中使用的是成员变量)。但使用全局变量就有悖于 C++ 封装的特性,之所以搞这么复杂,还是为了封装得好用一些。

个人觉得这一种方法过于复杂了一点,没有上一章中的两种方法直观,这里就权当做个参考吧。

参考资料
C++接口工程实践:有哪些实现方法?
C++二进制兼容

标签:std,NetworkV4Impl,二进制,NetworkV4,C++,兼容,str,解析,IMPL
From: https://www.cnblogs.com/sggggr/p/16951843.html

相关文章

  • c++ vector resize 和 assign
    resize改变大小resize(n,value),不够的部分填充为valuevector<int>nums{1,2,3,4,5,6};nums.resize(3,100);//size缩小,保持原状->{1,2,3}nums.resi......
  • 源码解析:Dubbo3 的 Spring 适配原理与初始化流程
    Dubbo国内影响力最大的开源框架之一,非常适合构建大规模微服务集群的,提供开发框架、高性能通信、丰富服务治理等能力。同时Dubbo无缝支持Spring、SpringBoot模式的开......
  • C++获取一年中所有周信息
    std::map<int,std::pair<CTime,CTime>>YearOfWeek(intyear){std::map<int,std::pair<CTime,CTime>>weekMap;CTimetime(year,1,1,0,0,0);in......
  • 两道面试题,带你解析Java类加载机制
    在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题:classGrandpa{static{System.out.println("爷爷在静态代码块");}}classFatherextendsGra......
  • VC6.0和VS2005:C++和C#编写调用COM组件
    这篇文章就是关于COM组件的编写和调用的,主要包含了使用VC6.0编写和调用COM组件,VS2005中使用C#编写和调用COM组件,以及在VC6.0和VS2005之间互相调用COM组件。前一阵在......
  • c++ hello world
    //myfirst.cpp--displaysamessage#include<iostream>//aPREPROCESSORdirectiveintmain()//functionheader{......
  • C++ IMPL模式解析
    https://blog.csdn.net/myw31415926/article/details/127722899抛砖引玉试想一个问题,如果有一套收发数据的网络接口,需要提供给其他同事或厂家使用,包含头文件和动态库,假设......
  • 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......