【问题标题】:Function inheritance and return types in C++C++中的函数继承和返回类型
【发布时间】:2020-11-07 00:58:04
【问题描述】:

我正在学习 C++,遇到了一个可以用我以前的编程经验(主要是 C 和 Java;一些但有限的 OOP 经验)解决的问题,但我想知道什么才是合适的、现代的C++ 解决方案。问题涉及具有不同返回类型的虚函数的继承和派生类版本。基于多个 Stack Overflow 线程,这样的事情是不可能的。那么接下来我该怎么做呢?

为了练习 C++ 功能,我正在编写一个光线追踪器。我有一个虚拟基类Object 和派生类Polyhedron 和Polygon 来描述光可以与之交互的对象Rays。 (实际上,我有中间虚拟类 Solid 和 Face, 以及派生类 Sphere、Cylinder、Circle 以及 Polyhedron 和 Polygon,但为了简单起见,让我们在这里忘记它们。)目前,我只实现了光的发射和吸收,即Ray 只在没有任何折射或反射的情况下直行。 Polyhedron 内的吸收与强度(指数衰减)成正比,因此我必须找出 Ray 穿过的物体,并将 Ray 的强度从其源向前整合到它撞击探测器的位置。我有一个向量std::vector<std::shared_ptr<Intersection>> intersections 来存储Ray 与模拟场景中的对象的所有这些交集。一个交叉点需要包含交叉点Points、交叉点Polygon 面和Polyhedron 本身用于Polyhedron 对象,或者交叉点Point 和Polygon 面本身用于Polygon目的。因此,我希望派生类Intersection_Polyhedron 和Intersection_Polygon 来覆盖对Intersection::modulate_intensity(const double intensity_before) const 的调用,这应该在传递相关对象后返回Ray 的强度。换句话说,我想避免检查相交对象的类型,而是在计算对Ray 强度的调制时利用继承。

我想让每个Ray 简单地循环一个向量std::vector<std::shared_ptr<Object>> objects,其中包含模拟场景中的所有对象,调用虚函数Object::get_intersection(const Ray& ray) const 并得到Intersection_Polyhedron 或Intersection_Polygon 作为回报交叉点的类型(如果它带有 Polyhedron 或 Polygon)。指向这些派生交集对象的指针将被推回intersections,intersections 将根据与Ray 原点的距离进行排序,然后循环调用并覆盖Intersection::modulate_intensity() 以确定Ray的探测器上的最终强度。对我来说,这听起来像是实现此目的的 C++/OOP 方式,但似乎不可能,因为派生类的基类虚函数版本必须都具有相同的返回类型。那我该怎么做呢?

(目前,我从get_intersection() 为Polyhedrons 和Polygons 返回一个单一类型的Intersection。作为其成员,Intersection 具有相交Points 和相交std::shared_ptr<Polygon> 面的向量和std::shared_ptr<Polyhedron>(这是nullptr 用于Polygons,因为没有大容量)。为了区分Polyhedrons 和Polygons 的交叉点,我简单地检查是否有一个或两个交叉点Points . 这不是太不优雅,但现代 C++ 必须提供更好的方法来实现这一点,对吧?)

一些非常类似于 C++ 的伪代码来进一步阐明我想要实现的目标:

// ...

// create objects in a scene
std::vector<std::shared_ptr<Object>> objects;
// ...

// find a ray's intersections with the objects
std::vector<std::shared_ptr<Intersection>> intersections;
for(const auto& object : objects) {
  // virtual class Object's function overridden with that of Polyhedron or Polygon
  // returns std::shared_ptr<Intersection_Polyhedron> or std::shared_ptr<Intersection_Polygon> based on type of object
  auto intersection = object->get_intersection(ray);
  intersections.push_back(intersection);
}

// sort the intersections with std::sort and a lambda expression
// ...

// calculate a ray's intensity
double intensity = 0.0;
for(const auto& intersection : intersections) {
  // virtual class Intersection's function overridden with that of Intensity_Polyhedron or Intensity_Polygon
  intensity = intersection->modulate_intensity(intensity);
}

// ...

【问题讨论】:

  • 由于Intersection是Intersection_Polyhedron和Intersection_Polygon的基类,object-&gt;get_intersection(ray)都可以为虚方法返回std::shared_ptr&lt;Intersection&gt;...
  • 您是否暗示Polyhedron::get_intersection() 会构造一个Intersection_Polyhedron 但会返回一个std::shared_ptr&lt;Intersection&gt;,而Polygon::get_intersection() 会构造一个Intersection_Polygon 但也会返回一个std::shared_ptr&lt;Intersection&gt; 给它?然后当intersection-&gt;modulate_intensity() 被调用时,它会被Intersection_Polyhedron::modulate_intensity() 或Intersection_Polygon::modulate_intensity() 覆盖。我想知道为什么我没有想到这一点。我猜这些新的继承和智能指针的东西已经融化了我的大脑......

标签: c++ inheritance overriding


【解决方案1】:

返回界面一般都可以:

class Ray;

struct Intersection
{
    virtual ~Intersection() = default;
    virtual double modulate_intensity(double intensity) = 0;
};
struct Intersection_Polygon : Intersection
{
    double modulate_intensity(double intensity) override {/**/}
};
struct Intersection_Polyhedron : Intersection
{
    double modulate_intensity(double intensity) override {/**/}
};

struct Object
{
    virtual ~Object() = default;
    virtual std::shared_ptr<Intersection> get_intersection(const Ray&) = 0;
};

struct Polygon : Object
{
    std::shared_ptr<Intersection> get_intersection(const Ray&) override {
        return std::make_shared<Intersection_Polygon>();
    }
};
struct Polyhedron : Object
{
    std::shared_ptr<Intersection> get_intersection(const Ray&) override {
        return std::make_shared<Intersection_Polyhedron>();
    }
};

可以通过协方差改进返回类型,但 C++ 仅处理引用和(非智能)指针。所以它需要一些样板来模拟它的智能指针:

struct Object
{
    virtual ~Object() = default;
    std::shared_ptr<Intersection> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<Intersection>{get_intersection_ptr(ray)};
    }
protected:
    virtual Intersection* get_intersection_ptr(const Ray&) = 0;
};

struct Polygon : Object
{
    std::shared_ptr<Intersection_Polygon> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<Intersection_Polygon>{get_intersection_ptr(ray)};
    }
protected:
    Intersection_Polygon* get_intersection_ptr(const Ray&) override {
        return new Intersection_Polygon();
    }
};

模板(可能是 CRTP)可能有助于分解样板:

template <typename IntersectionType>
struct ObjectT : Object
{
    std::shared_ptr<IntersectionType> get_intersection(const Ray& ray)
    {
        return std::shared_ptr<IntersectionType>{get_intersection_ptr(ray)};
    }
protected:
    IntersectionType* get_intersection_ptr(const Ray&) override {
        return new IntersectionType();
    }
};

struct Polygon : ObjectT<Intersection_Polygon> {};
struct Polyhedron : ObjectT<Intersection_Polyhedron> {};

【讨论】:

  • 感谢您阅读我冗长的问题并提供如此明确的答案。您的解决方案的第一部分似乎运行良好,在进一步消化后,我将尝试协方差和模板改进。
猜你喜欢
  • 2016-09-13
  • 2012-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
相关资源
最近更新 更多