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

时间:2023-01-15 20:39:36

I would like to do this:

我想这样做:

class Derived;

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

class Derived : public Base {
};

Of course this doesn't work since I can't return an incomplete type. But neither can I define Derived before base, since I can't inherit from an incomplete type either. I figure that I could use templates as a workaround (using Derived as a template argument to Base), but it seems a really ugly way of doing things. Might there be another way?

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

Elaboration: I'm writing a raytracer, and each Shape class has a function which returns its bounding box. However, I've made the BBox a subclass of Shape, so I can visualize it. Is this bad design?

详细说明:我正在编写一个光线跟踪器,每个Shape类都有一个返回其边界框的函数。但是,我已经将BBox作为Shape的子类,因此我可以将其可视化。这是不好的设计吗?

6 个解决方案

#1


9  

There's nothing wrong with the code in your question. This

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

class Derived;

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

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

should compile just fine. However, callers of 'Base::f()' will need to have seen the definition of 'Derived`.

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

#2


8  

You could use a pointer (or a reference):

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

class Derived;

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

class Derived : public Base {
};

But this is code smell to me. Why should anybody inheriting from this class need to know about another derived class? In fact, why should the base class be concerned with it's derivee's?

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

For your situation, you'll need to notice things that mgiht be a signal for bad design. Although it makes sense that your bounding box would derive from Shape, keep in mind, since Shape has a function that returns a bounding box, a bounding box will have a function that returns itself.

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

I'm not sure the best solution, but you could make BBox a separate class altogether, and perhaps give it a function akin to: Shape *as_shape(void) const, which would construct a class Box : public Shape with the same dimensions as the bounding box.

我不确定最好的解决方案,但你可以完全使BBox成为一个单独的类,并且可能给它一个类似于:Shape * as_shape(void)const的函数,它将构造一个类Box:public Shape,其尺寸与边界框。

I still feel there is a better way, but I'm out of time for now, I'm sure someone else will think of a better solution.

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

#3


4  

Why not just do:

为什么不这样做:

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

#4


4  

Your notion about templates wasn't necessarily a bad one. What you describe is called the Curiously Recurring Template Pattern.

你对模板的看法不一定是坏的。你所描述的被称为奇怪的重复模板模式。

An example:

#include <iostream>

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

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

#5


3  

I'd go with returning a pointer to a Base, so that Base doesn't need to know about Derived or anything else that comes along later:

我将返回指向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;
}

#6


2  

As others have pointed out, the code sample you have can be made to work, but that you probably mean to return a pointer to the base class from f().

正如其他人所指出的那样,你可以使你的代码示例工作,但你可能意味着从f()返回一个指向基类的指针。

In your elaboration, you mention that the bounding box is a subclass of shape, but there is a problem:

在您的详细说明中,您提到边界框是形状的子类,但是存在一个问题:

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?
};

Lets move some of the responsibilities around:

让我们解决一些责任:

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

Your application will now probably need to hold collections of BoundedShape* and occasionally ask one for its BBox*.

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

#1


9  

There's nothing wrong with the code in your question. This

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

class Derived;

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

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

should compile just fine. However, callers of 'Base::f()' will need to have seen the definition of 'Derived`.

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

#2


8  

You could use a pointer (or a reference):

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

class Derived;

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

class Derived : public Base {
};

But this is code smell to me. Why should anybody inheriting from this class need to know about another derived class? In fact, why should the base class be concerned with it's derivee's?

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

For your situation, you'll need to notice things that mgiht be a signal for bad design. Although it makes sense that your bounding box would derive from Shape, keep in mind, since Shape has a function that returns a bounding box, a bounding box will have a function that returns itself.

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

I'm not sure the best solution, but you could make BBox a separate class altogether, and perhaps give it a function akin to: Shape *as_shape(void) const, which would construct a class Box : public Shape with the same dimensions as the bounding box.

我不确定最好的解决方案,但你可以完全使BBox成为一个单独的类,并且可能给它一个类似于:Shape * as_shape(void)const的函数,它将构造一个类Box:public Shape,其尺寸与边界框。

I still feel there is a better way, but I'm out of time for now, I'm sure someone else will think of a better solution.

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

#3


4  

Why not just do:

为什么不这样做:

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

#4


4  

Your notion about templates wasn't necessarily a bad one. What you describe is called the Curiously Recurring Template Pattern.

你对模板的看法不一定是坏的。你所描述的被称为奇怪的重复模板模式。

An example:

#include <iostream>

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

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

#5


3  

I'd go with returning a pointer to a Base, so that Base doesn't need to know about Derived or anything else that comes along later:

我将返回指向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;
}

#6


2  

As others have pointed out, the code sample you have can be made to work, but that you probably mean to return a pointer to the base class from f().

正如其他人所指出的那样,你可以使你的代码示例工作,但你可能意味着从f()返回一个指向基类的指针。

In your elaboration, you mention that the bounding box is a subclass of shape, but there is a problem:

在您的详细说明中,您提到边界框是形状的子类,但是存在一个问题:

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?
};

Lets move some of the responsibilities around:

让我们解决一些责任:

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

Your application will now probably need to hold collections of BoundedShape* and occasionally ask one for its BBox*.

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