没有虚拟析构函数的继承

Inherit Without Virtual Destructor

我在一个项目中使用了两个 classes。一个 class、Callback 负责保存来自回调的信息。另外一个class,UserInfo,就是暴露给用户的信息。基本上,UserInfo 应该是一个非常薄的包装器,它读取 Callback 数据并将其提供给用户,同时还提供一些额外的东西。

struct Callback {
  int i;
  float f;
};

struct UserInfo {
  int i;
  float f;

  std::string thekicker;
  void print();
  UserInfo& operator=(const Callback&);
};

问题是向 Callback 添加成员需要对 UserInfo 进行相同的更改,以及更新 operator= 和类似的依赖成员函数。为了让它们自动同步,我想这样做:

struct Callback {
  int i;
  float f;
};

struct UserInfo : Callback{
  std::string thekicker;
  void print();
  UserInfo& operator=(const Callback&);
};

现在 UserInfo 保证拥有与 Callback 相同的所有数据成员。事实上,踢球者是数据成员 thekickerCallback 中没有声明虚拟析构函数,我相信其他编码人员希望它保持这种状态(他们强烈反对虚拟析构函数的性能损失)。但是,如果通过 Callback* 销毁 UserInfo 类型,thekicker 将被泄露。应该注意的是,UserInfo 并不打算通过 Callback* 接口使用,因此这些 class 最初是分开的。另一方面,为了修改一个结构而不得不以相同的方式更改三段或更多段代码感觉不雅且容易出错。

问题:有什么方法可以让UserInfo公开继承Callback(用户必须能够访问所有相同的信息)但由于缺少虚拟析构函数,不允许将 Callback 引用分配给 UserInfo?我怀疑这是不可能的,因为它首先是继承的基本目的。我的第二个问题,有没有办法通过其他方法使这两个 class 保持同步?我想让 Callback 成为 UserInfo 的成员而不是 class 的父成员,但我希望使用 user.i 而不是 user.call.i 直接读取数据成员。

我想我问的是不可能的事情,但我一直对 Whosebug 答案的魔力感到惊讶,所以我想我只是问问是否真的有补救办法。

struct UserInfo : Callback {
  ...
  // assignment from Callback disallowed
  UserInfo& operator=(const Callback&) = delete;
  ...
};

请注意,STL 的特点是很多 没有虚拟析构函数的继承。文档明确指出这些 类 并非旨在用作基础 类。

一些示例是 vector<>、set<>、map<> ....

另一种方法是考虑私有继承,同时提供访问器方法来显示回调(在这种情况下,您也可以使用更干净的封装)。

是的,您可以使用技巧使成员保持同步并自动更新 operator=。虽然它很丑陋,涉及宏和使用包含文件的不寻常方式。

CallBackMembers.h:

MEMBER(int, i)
MEMBER(float, f)

其他地方:

struct Callback {
  #define MEMBER(TYPE,NAME) TYPE NAME;
  #include "CallbackMembers.h"
  #undef MEMBER
};

struct UserInfo {
  #define MEMBER(TYPE,NAME) TYPE NAME;
  #include "CallbackMembers.h"
  #undef MEMBER

  std::string thekicker;

  void print();    // you can use the macro trick here too

  UserInfo& operator=(const Callback& rhs)
  {
    #define MEMBER(TYPE,NAME) NAME = rhs.NAME;
    #include "CallbackMembers.h"
    #undef MEMBER
    return *this;
  }
};

您始终可以通过在基 class:[=12 中保护析构函数来强制执行您提到的(在某种程度上)“无法通过基 class 指针删除”约束=]

// Not deletable unless a derived class or friend is calling the dtor.
struct Callback {
  int i;
  float f;
protected:
  ~Callback() {}
};

// can delete objects of this type:
struct SimpleCallback : public Callback {};


struct UserInfo : public Callback {
  std::string thekicker;
  // ...
};

正如其他人提到的,您可以删除赋值运算符。对于 c++11 之前的版本,只需将该函数的未实现原型设为私有即可:

private:
  UserInfo& operator=(const Callback&);

无法满足您想要的所有条件。

我个人认为你的想法是让它成为会员然后使用 user.call.i 是最好和最明确的选择。请记住,您编写的代码只使用一次,但您可以在可维护性(因为您的 UserData 永远不必更改)和可读性(因为它对最终使用哪个属性是 100% 透明的)方面弥补了它回调数据的一部分,是辅助数据)。

唯一可能有意义的其他选项是改用私有继承,并将 using 属性或函数 UserData。有了这个,当新数据被添加到回调时,你仍然需要添加一个 using,但是你得到了客户端所需的 user.i 语法。