非多态派生类的基类

Base class of non polymorphic derived classes

本文关键字:基类 派生 多态      更新时间:2023-10-16

我有以下类定义:

class BaseHandle { /* Lots of things */ };
class VertexHandle : public BaseHandle {
    /* Only static members and non-virtual functions, default dtor */ };
class EdgeHandle : public BaseHandle { /* Dito */ };
class FaceHandle : public BaseHandle { /* Dito */ };

所有类都没有虚函数或基。
派生类仅派生自BaseHandle,不添加任何非静态成员,也不添加非默认 dtor。

我想在同一向量中保存Vertex-Edge-FaceHandles

std::vector<BaseHandle*> handles;

但是它不起作用,如果我检索BaseHandle对象并想将它们dynamic_cast派生对象,它会失败,因为这些类不是多态的(这是我的解释,也许我错了)。

如何实现BaseHandles的共同向量?我应该提到,我不能更改类定义,因为它们是第三方库的一部分。

你需要在父类中有一个虚拟析构函数才能多态地使用它

class BaseHandle 
{
  public:
    virtual ~BaseHandle();
  ...
};

这是因为dynamic_cast使用 RTTI(运行时类型信息),该信息仅在您的类至少有一个虚拟成员函数时才可用

这也将防止资源泄漏,否则只会销毁实例的父类部分


解决方法

您可以使用std::shared_ptr std::vector,您不仅可以通过不必手动调用newdelete来避免内存泄漏,而且智能指针还具有神奇的属性(它存储删除器以根据其构造方式调用销毁)来解决您的问题:

int main()
{
  std::vector<std::shared_ptr<BaseHandle>>      shared_vec;
  shared_vec.push_back(std::make_shared<VertexHandle>());
} // At the end of scope all destructors are called correctly

如果您无法访问 c++11,您可以使用boost::shared_ptr

你可以存储

struct thing
{
    enum Type { vertex, edge, face };
    Type type;
    union
    {
        VertexHandle * vh;
        EdgeHandle * eh;
        FaceHandle * fh;
    };
};

但这基本上是一团糟...您确定要这样做吗? 看起来您在单个数组中存储了多个类型,尽管事实上无法多态地使用它们,那么实际上是否有充分的理由只有一个数组,而不是三个数组?

继Kerrek的评论之后。您可以"创建自己的并行类层次结构,并将每种类型添加为成员"。例如:

class MyBaseHandle {
 public:
  virtual ~MyBaseHandle(){}
  virtual Box getBoundingBox() const = 0;
};
class MyEdgeHandle : public MyBaseHandle {
  std::unique_ptr<EdgeHandle> handle_;
 public:
  MyHandle(std::unique_ptr<EdgeHandle> handle) : handle_(std::move(handle)) {}
  Box getBoundingBox() const override;
};

然后,如果需要,您可以dynamic_cast。但我会尽量避免使用dynamic_cast。在并行类层次结构中添加virtual方法,以执行所需的操作。例如,我向基类添加了一个 virtual getBoundingBox 函数,然后您可以专门针对特定类型的句柄:

Box MyEdgeHandle::getBoundingBox() const {
  // Get data from EdgeHandle
  auto v1 = handle_->getVertex1();
  auto v2 = handle_->getVertex2();
  // create box from edge data...
  return box;
} 

现场演示

如果从BaseHandle派生的所有类都只使用来自BaseHandle的单一继承(加上可能从具有普通 dtor 的空类继承,这些空类受空基类优化的影响),并且除了非virtual函数和static成员之外不添加任何内容,并且所有派生类都使用默认 DTOR 或等效项,则可以只static_cast目标。

尽管请注意,没有办法知道它实际上是哪个派生类(如果有的话)。