是否可以从C ++中的基类方法返回派生类?

时间:2009-09-11 17:46:25

标签: c++ class inheritance

我想这样做:

class Derived;

class Base {
    virtual Derived f() = 0;
};

class Derived : public Base {
};

当然这不起作用,因为我不能返回不完整的类型。但是我也不能在base之前定义Derived,因为我也不能从不完整的类型继承。我认为我可以使用模板作为解决方法(使用Derived作为Base的模板参数),但这似乎是一种非常难看的做事方式。可能还有另一种方式吗?

详细说明:我正在编写一个光线跟踪器,每个Shape类都有一个返回其边界框的函数。但是,我已经让BBox成为Shape的子类,所以我可以想象它。这是一个糟糕的设计吗?

6 个答案:

答案 0 :(得分:9)

您的问题中的代码没有任何问题。此

class Derived;

class Base {
    virtual Derived f() = 0;
};

class Derived : public Base {
    virtual Derived f() {return Derived();}
};

应该编译得很好。但是,'Base :: f()'的调用者需要看到'Derived`的定义

答案 1 :(得分:8)

您可以使用指针(或引用):

class Derived;

class Base {
    virtual Derived *f() = 0;
};

class Derived : public Base {
};

但这对我来说是代码味道。为什么从这个类继承的人需要知道另一个派生类?事实上,基类为什么要关注它的衍生物?

根据您的情况,您需要注意那些不良设计的信号。虽然你的边界框来自Shape是有意义的,但请记住,因为Shape有一个返回边界框的函数,边界框将有一个返回自身的函数。

我不确定最佳解决方案,但您可以将BBox完全单独添加,并为其提供类似于Shape *as_shape(void) const的函数,它将构建class Box : public Shape与边界框尺寸相同。

我仍然觉得有更好的方法,但我现在没时间了,我相信其他人会想到更好的解决方案。

答案 2 :(得分:4)

为什么不这样做:

class Base {
    virtual Base *f() = 0;
};

答案 3 :(得分:4)

你对模板的看法不一定是坏的。您描述的内容称为Curiously Recurring Template Pattern

一个例子:

#include <iostream>

template <typename T>
struct Base
{
    virtual T* foo() = 0;
};

struct Derived : Base<Derived>
{
    virtual Derived* foo() { return this; }
};

答案 4 :(得分:3)

我会返回一个指向Base的指针,因此Base不需要知道Derived或后来出现的任何其他内容:

class Base {
  virtual Base *f() = 0;
};

class Derived : public Base {
  virtual Base *f();
};

Base *Derived::f() {
  Derived *r = new Derived;
  return r;
}

答案 5 :(得分:2)

正如其他人所指出的那样,的代码示例可以可以工作,但是你可能想要从f()返回指向基类的指针。

在你的阐述中,你提到边界框是形状的子类,但是有一个问题:

class Shape{
    virtual Shape* getBoundingBox() = 0;
};

class Square: public Shape{
    virtual Shape* getBoundingBox();
};
class BBox: public Shape{
    virtual Shape* getBoundingBox(); // Whoops! What will BBox return?
};

让我们解决一些责任:

class Shape{
    virtual void draw() = 0; // You can draw any shape
};
class BBox: public Shape{
    virtual void draw(); // This is how a bounding box is drawn
};

class BoundedShape: public Shape{
    virtual BBox* getBoundingBox() = 0; // Most shapes have a bounding box
};

class Square: public BoundedShape{
    virtual void draw();
    virtual BBox* getBoundingBox(); // This is how Square makes its bounding box
};

您的应用程序现在可能需要保留BoundedShape*的集合,并偶尔询问其BBox*