共享库中的 D 指针和组合 类

D-pointer and composition classes in a shared library

我正在用 Qt5 C++ 创建一个共享库。为了允许未来的更新保留二进制兼容性,我想使用 d 指针技术。但是,当有classes的组合时,我不知道如何应用它。我找到的例子,包括 here,只解释了 class 继承的情况。我的问题是

Do I need to make a corresponding private class for each class in the library (myLib, B and C) or only for the main one (myLib) and how to access them later?

这是我的设置和没有私有 classes 的所需功能:

myLib.h

#include "B.h"

class myLib;
{
public:
        myLib();
        B *getB(int n);

private:
        QList<B *> m_b;
}

B.h

#include "C.h"

class B;
{
public:
        B();
        C *getC(int n);
private:
        QList<C *> m_c;
}

C.h

class C;
{
public:
        C();
        int getVar();
private:
        int m_var;
}

在主应用的某处:

myLib *m_lib = new myLib();
int k = m_lib->getB(4)->getC(2)->getVar();

来自链接:问题 "Never change the size of an exported C++ class"。
解决方案:"The trick is to keep the size of all public classes of a library constant by only storing a single pointer. This pointer points to a private/internal data structure that contains all the data."。

只要你的 class 不显示给你的库的使用者,就可以随意 D-pointerless。 "shown to the consumers" 我的意思是 "with their full definition made available by declarations in the headers meant to be included by the consumer code"。可能 public/private 一词在这里受到了 'semantic overload' 的影响,让我们使用 'exposed'/'opaque'(参见 ** 脚注)

在您的示例中,BC 都公开了,因此它们必须可用 "by pointers only"。

myLibclass也是如此。更糟糕的是:myLib 的实例可以通过值获取,因为构造函数是 public。这意味着我可以做类似的事情:

myLib libObj;
libObj.getB(4)->getC(2)->getVar();

这将使 myLib 的未来版本无法获得 "drop in replacements, no recompilation needed"。


我建议强制消费者通过工厂方法获取 myLib(或 'singleton')的实例。在线内容:

class myLib {
private:
  myLib() {
  }

public:

  static myLib* createInstance() {
    return new myLib();
  }
};

** 例如 "exposed/opaque declaration" - class B 暴露给图书馆消费者(他们知道 B-s 会有..咳咳...私人部分),但大约 class M 消费者只知道它存在并且图书馆将提供指向它的指针:

文件"myLib.hpp"

// M_type is a pointer to a class and that's all you,
// the consumer, need to know about it. You give me an M_type
// and ask specific questions about it and you'll
// get the details or access to data I choose to
// make available to you
typedef class M * M_type; 

// Dear the library consumer, this class is public to you.
// You know I'm keeping a list of M_type, even if you also know
// you'll never get you hands directly on that list, because
// it has a private access. But having this information,
// **you can compute the sizeof(B)**.
class B {
public:
  B();

  M_type getM(int n);

  const M_type getM(int n) const;

  // that is one of the "questions" you can ask about an M_type
  const char* getMLabel(const M_type var) const;

  // I'm providing you with access to something that allows
  // you to modify the amount stored by an M_type,
  // even if you don't know (and never will) how
  // I'm storing that amount
  int&        getMAmount(M_type var);

  // You don't need to know how to create M-s, I'll
  // be doing it for you and provide the index of the created
  // M_type. Ask me with getM to get that pointer.
  inr  registerM(const char* label, int amount);


private:
  QList<M_type> ems;
};

某处,在库代码的深处,将存在一个 header 来定义 class M 是什么,myLib.cpp 将包含它,但是 header 将仅用于编译库,从不随 myLib binary 版本提供。 因此,class M 对于图书馆消费者来说是不透明的(相对于公开的)。