在没有虚拟析构函数的情况下继承

时间:2015-01-12 18:05:06

标签: c++ c++11

我有两个在项目中使用的类。一个班级Callback负责从回调中保存信息。另一个类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相同的所有数据成员。事实上,踢球者是数据成员thekicker。在Callback中没有声明虚拟析构函数,我相信其他编码器希望它保持这种状态(他们强烈反对虚拟析构函数的性能损失)。但是,如果通过thekicker销毁UserInfo类型,Callback*将会泄露。应该注意的是,UserInfo并不打算通过Callback*接口使用它,因此为什么这些类首先是分开的。另一方面,为了修改一个结构而不得不以相同的方式改变三个或更多个代码,感觉不够优雅且容易出错。

问题:有没有办法允许UserInfo公开继承Callback(用户必须能够访问所有相同的信息),但不允许分配{ {1}}具体是因为缺少虚拟析构函数而引用Callback?我怀疑这是不可能的,因为它首先是继承的基本目的。我的第二个问题是,有没有办法通过其他方法使这两个类保持同步?我想让UserInfo成为Callback的成员而不是父类,但我想要使用UserInfo代替user.i来直接阅读数据成员。

我想我是在寻求不可能的事情,但我一直对stackoverflow答案的巫术感到惊讶,所以我想我只是想问一下是否真的有一个补救措施。

3 个答案:

答案 0 :(得分:3)

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

// 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;
  // ...
};

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

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

答案 1 :(得分:1)

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

请注意,STL具有很多的继承而没有虚拟析构函数。该文档明确声明这些类不是用作基类。

一些例子是vector<&gt ;, set<&gt ;, map<> ....

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

答案 2 :(得分:0)

是的,您可以使用一些技巧来保持成员同步并自动更新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;
  }
};