Можно ли вернуть производный класс из метода базового класса в C ++? - PullRequest
4 голосов
/ 11 сентября 2009

Я хотел бы сделать это:

class Derived;

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

class Derived : public Base {
};

Конечно, это не работает, так как я не могу вернуть неполный тип. Но я также не могу определить Derived перед базой, так как не могу наследовать от неполного типа. Я полагаю, что я мог бы использовать шаблоны в качестве обходного пути (используя Derived в качестве аргумента шаблона для Base), но это кажется действительно уродливым способом делать вещи. Может ли быть другой путь?

Разработка: я пишу raytracer, и у каждого класса Shape есть функция, которая возвращает ограничивающую рамку. Тем не менее, я сделал BBox подклассом Shape, чтобы я мог его визуализировать. Это плохой дизайн?

Ответы [ 6 ]

9 голосов
/ 11 сентября 2009

Нет ничего плохого в коде в вашем вопросе. Это

class Derived;

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

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

должно скомпилироваться просто отлично. Тем не менее, вызывающие 'Base :: f ()' должны были видеть определение из Derived

8 голосов
/ 11 сентября 2009

Вы можете использовать указатель (или ссылку):

class Derived;

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

class Derived : public Base {
};

Но это кодовый запах для меня. Почему кому-либо, унаследовавшему этот класс, нужно знать о другом производном классе? На самом деле, почему базовый класс должен быть связан с его производным?

В вашей ситуации вы должны заметить вещи, которые могут свидетельствовать о плохом дизайне. Хотя имеет смысл, что ваш ограничивающий прямоугольник будет получен из Shape, имейте в виду, поскольку Shape имеет функцию, которая возвращает ограничивающий прямоугольник, ограничивающий прямоугольник будет иметь функцию, которая возвращает себя.

Я не уверен, что лучшее решение, но вы могли бы сделать BBox отдельным классом в целом, и, возможно, дать ему функцию, похожую на: Shape *as_shape(void) const, которая создала бы class Box : public Shape с теми же измерениями, что и ограничительная коробка.

Я все еще чувствую, что есть лучший способ, но сейчас у меня нет времени, я уверен, что кто-то еще придумает лучшее решение.

4 голосов
/ 12 сентября 2009

Ваше представление о шаблонах не обязательно было плохим. То, что вы описываете, называется Любопытно повторяющимся шаблоном шаблона .

Пример:

#include <iostream>

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

struct Derived : Base<Derived>
{
    virtual Derived* foo() { return this; }
};
4 голосов
/ 11 сентября 2009

Почему бы просто не сделать:

class Base {
    virtual Base *f() = 0;
};
3 голосов
/ 11 сентября 2009

Я бы пошел с возвратом указателя на базу, так что базе не нужно знать о Derived или о чем-либо еще, что произойдет позже:

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

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

Base *Derived::f() {
  Derived *r = new Derived;
  return r;
}
2 голосов
/ 11 сентября 2009

Как уже отмечали другие, пример кода, который у вас есть , можно заставить работать, но вы, вероятно, намереваетесь вернуть указатель на базовый класс из 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*.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...