首页 > 解决方案 > C++中的函数继承和返回类型

问题描述

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

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

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

(目前,我为and返回一个奇异类型的Intersectionfrom 。作为它的成员, an具有相交和相交面的向量,以及 an (这是 s 的 a ,因为没有体积)。为了区分 and 的交叉点,我很简单检查是否有一个或两个交集。这不是太不优雅,但现代 C++ 必须提供更好的方法来实现这一点,对吗?)get_intersection()PolyhedronsPolygonsIntersectionPointsstd::shared_ptr<Polygon>std::shared_ptr<Polyhedron>nullptrPolygonPolyhedronsPolygonsPoints

一些非常类似于 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);
}

// ...

标签: c++inheritanceoverriding

解决方案


返回界面一般都很好:

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

template (possibly CRTP) might help to factorize boiler plate:

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

推荐阅读