重载基类非虚函数中使用的虚函数

发布于 2024-11-24 11:43:44 字数 3461 浏览 0 评论 0原文

嘿,我正在尝试构建 ConcavePolygon 类的以下成员函子,但由于某种原因,我收到链接器外部符号错误:

无法解析的外部符号“public: virtual void __thiscall sf::ConcavePolygon::Partition::RunAlgorithm(类 TPPLPoly &,类 std::列表> &)"

我的目标是简单地创建一个包含 SFML(lib)可以理解的图形数据的类,以及对图形数据进行分区或三角剖分(多边形)的函数,

而不是使用非常相似的代码编写两个大型函数; 一个用于三角化,一个用于做我所说的凸面,我决定尝试使用函子,并使用后代三角化和制作基本函子分区Convexulate

基类 Partition 仅包含两个函数:

  • 包含所有功能的构造函数
  • RunAlgorithm 函数(与构造函数分开,因此可以由后代重载)

我认为该错误与此有关。使用虚拟 RunAlgorithm 函数,因为它是构造函数所依赖的,但我猜测它会被后代以某种方式呈现为无效。

我该怎么做才能实现我的目标或解决这个问题?

这是代码:

class ConcavePolygon : public Body{ 
protected:
    std::list<Vector2f> SFMLPoints;
    std::vector <TPPLPoint> TPPLPoints; //TODO: figure out how to make a temp version without Memory Exception

public:
//////////////////// Partitioning/Triangulating Classes /////////////////////////////////////////////////////////////
    class Partition{
    public:
        virtual void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput);

        Partition(){};
        Partition(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){ //TODO turn this into a base class for triangulate or Convexulate

        //rev up all the needed data structs
        std::list<TPPLPoly> PartitionOutput;
        std::list <TPPLPoly> ::iterator I;

        //Backup the points, and convert them to tppl
        for(int I=0; I<numbPoints; I++){
            Poly->TPPLPoints.push_back(TPPLPoint(Points[I].x, Points[I].y));
            Poly->SFMLPoints.push_back(Points[I]);}
        TPPLPoly TempPoly(&Poly->TPPLPoints[0], numbPoints, false);

        //clear everything to be filled with the Partition Algorithm
        Poly->Clear();

        // Run the Partitioning Algorithm (This is an abstract function, and is overloaded)
        RunAlgorithm(TempPoly, PartitionOutput);

        // Convert results to SFML points, shapes, and add to the body
        for( I= PartitionOutput.begin(); I!= PartitionOutput.end();I++){
            sf::Shape TempShape;
            for(int i=0; i< I->GetNumPoints(); i++)
                TempShape.AddPoint( I->GetPoint(i).x, I->GetPoint(i).y);
            Poly->AddShape(TempShape);
        }
    };
};

    class Convexulate: public Partition{
    public:
        Convexulate(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){
            Partition(Poly, Points, numbPoints);};

        void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput){
            TPPLPartition Partition;
            Partition.ConvexPartition_OPT(&Poly, &PartitionOutput);
        };
    };

    class Triangulate: public Partition{
    public:
        Triangulate(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){
            Partition(Poly, Points, numbPoints);};

        void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput){
            TPPLPartition Partition;
            Partition.Triangulate_OPT(&Poly, &PartitionOutput);
        };
    };

//////////////////////  Constructors    /////////////////////////////////////////////////////
    ConcavePolygon(Vector2f* Points, long numbPoints){
        Convexulate(this,Points, numbPoints);
    };


};// ConcavePolygon Class

Hey so i'm trying to build the following member Functors of class ConcavePolygon, and i'm getting Linker External symbol error for some reason:

unresolved external symbol "public: virtual void __thiscall
sf::ConcavePolygon::Partition::RunAlgorithm(class TPPLPoly &,class
std::list > &)"

My goal was to simply make a class that holds graphical data that SFML (lib) can understand, and functions to either Partition or Triangulate the graphical data(Polygon(s))

Instead of writing two large functions with VERY similar code; one to Triangulate, and one to do what i call Convexulating, I decided attempt working with functors, and made the base functor Partition, with Descendents Triangulate and Convexulate.

The base class Partition contains only two functions:

  • The Constructor which contains all the functionality
  • The RunAlgorithm function(seperated from the constructor so it could be overloaded by descendents)

I figure the error has something to do with the virtual RunAlgorithm function as it is depended on by the Constructor, yet i'm guessing it's rendered invalid somehow by descendents.

What do i do to achieve my goal or fix this?

Here's the code:

class ConcavePolygon : public Body{ 
protected:
    std::list<Vector2f> SFMLPoints;
    std::vector <TPPLPoint> TPPLPoints; //TODO: figure out how to make a temp version without Memory Exception

public:
//////////////////// Partitioning/Triangulating Classes /////////////////////////////////////////////////////////////
    class Partition{
    public:
        virtual void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput);

        Partition(){};
        Partition(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){ //TODO turn this into a base class for triangulate or Convexulate

        //rev up all the needed data structs
        std::list<TPPLPoly> PartitionOutput;
        std::list <TPPLPoly> ::iterator I;

        //Backup the points, and convert them to tppl
        for(int I=0; I<numbPoints; I++){
            Poly->TPPLPoints.push_back(TPPLPoint(Points[I].x, Points[I].y));
            Poly->SFMLPoints.push_back(Points[I]);}
        TPPLPoly TempPoly(&Poly->TPPLPoints[0], numbPoints, false);

        //clear everything to be filled with the Partition Algorithm
        Poly->Clear();

        // Run the Partitioning Algorithm (This is an abstract function, and is overloaded)
        RunAlgorithm(TempPoly, PartitionOutput);

        // Convert results to SFML points, shapes, and add to the body
        for( I= PartitionOutput.begin(); I!= PartitionOutput.end();I++){
            sf::Shape TempShape;
            for(int i=0; i< I->GetNumPoints(); i++)
                TempShape.AddPoint( I->GetPoint(i).x, I->GetPoint(i).y);
            Poly->AddShape(TempShape);
        }
    };
};

    class Convexulate: public Partition{
    public:
        Convexulate(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){
            Partition(Poly, Points, numbPoints);};

        void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput){
            TPPLPartition Partition;
            Partition.ConvexPartition_OPT(&Poly, &PartitionOutput);
        };
    };

    class Triangulate: public Partition{
    public:
        Triangulate(ConcavePolygon* Poly, Vector2f* Points, long numbPoints){
            Partition(Poly, Points, numbPoints);};

        void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput){
            TPPLPartition Partition;
            Partition.Triangulate_OPT(&Poly, &PartitionOutput);
        };
    };

//////////////////////  Constructors    /////////////////////////////////////////////////////
    ConcavePolygon(Vector2f* Points, long numbPoints){
        Convexulate(this,Points, numbPoints);
    };


};// ConcavePolygon Class

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我们的影子 2024-12-01 11:43:44

看起来在您的 Partition 后代中,您没有正确地将调用转发到 Partition 构造函数。相反,您正在构造一个临时 Partition 对象,然后立即将其丢弃。

struct Base {
    Base() { cout << "Base Default" << endl; }
    Base(int i) { cout << "Base Int: " << i << endl;
    ~Base() { cout << "~Base" << endl;
}

struct DerivedWrong : Base {
    DerivedWrong(int i) {
        Base(i);
        cout << "Derived Int: " << i << endl;
    }
};

如果构造一个 DerivedWrong 对象,则输出应该是

Base Default
Base Int: 5
~Base
Derived Int: 5

See that Snake destructor call before the output in the导出类构造函数?那是被销毁的临时对象。在该临时对象的构造函数中,它尝试调用您没有实现的 RunAlgorithm,因此您的链接器错误。如果您将 RunAlgorithm 设为纯虚拟[顺便说一句,您应该这样做],您将收到有关构造抽象类型的错误,而不是链接器错误,这可能对您更有用。从理论上讲,以下内容可以解决该问题:

struct DerivedRight : Base {
    DerivedRight(int i)
      : Base(i)
    { cout << "Derived Int: " << i << endl; }
};

在这种情况下,输出就是您所期望的。

Base Int: 5
Derived Int: 5

但是,仍然存在一个问题:您无法从基类构造函数调用虚函数并获得多态行为...您最终会调用该函数的基类版本,无论它是否已被重写。您必须等到对象完全构造完毕才能获得多态行为。

基本上,在构建的每个步骤中,对象就是正在构建的任何东西。尽管它最终将成为 Triangulate 对象,但在 Partition 的构造函数中,它的行为与 Partition 相同。这意味着您仍然会遇到链接器错误。

请注意,您仍然可以从构造函数中调用虚拟方法,您只需非常注意这种行为即可。如果您非常确定 Triangulate 等永远不会派生,您可以从基类构造函数中提取代码,并将其放入 Init 方法中,该方法将被调用来自派生类的构造函数。在该方法中,虚拟调度将按需要运行。

您还可以考虑延迟初始化...存储输入参数,并且仅在第一次执行函子时执行计算。之后,只需返回缓存的结果即可。这需要大量的额外开销,但好处是完全安全,无论您的最终继承图最终是什么样子。不过,有状态函子也有自己的问题,具体取决于您计划如何使用此类。

It looks like in your descendants of Partition, you are not properly forwarding the call to the Partition constructor. Instead, you are constructing a temporary Partition object, and then immediately throwing it away.

struct Base {
    Base() { cout << "Base Default" << endl; }
    Base(int i) { cout << "Base Int: " << i << endl;
    ~Base() { cout << "~Base" << endl;
}

struct DerivedWrong : Base {
    DerivedWrong(int i) {
        Base(i);
        cout << "Derived Int: " << i << endl;
    }
};

If you construct a DerivedWrong object, the output should be

Base Default
Base Int: 5
~Base
Derived Int: 5

See that sneaky destructor call before the output in the derived class constructor? That's the temporary object being destroyed. In that temporary object's constructor, it tries to call RunAlgorithm, which you do not implement, hense your linker error. Had you made RunAlgorithm pure virtual [which you should do, by the way] you would have gotten an error about constructing an abstract type, instead of the linker error, which might have been more useful to you. The following would theoretically fix the problem:

struct DerivedRight : Base {
    DerivedRight(int i)
      : Base(i)
    { cout << "Derived Int: " << i << endl; }
};

In this case, the output is what you would expect.

Base Int: 5
Derived Int: 5

However, there is still an issue: you can not call virtual functions from base class constructors and get polymorphic behavior... you end up calling the base class version of the function, whether it has been overrided or not. You have to wait until the object is fully constructed before you can get polymorphic behavior.

Basically, at every step of construction, the object is whatever is being constructed. Even though it will eventually be a Triangulate object, within Partition's constructor, it behaves as a Partition. Which means you will still end up with a linker error.

Note that you can still call virtual methods from within constructors, you just have to be very aware of this behavior. If you are quite certain that Triangulate et al will never be derived from, you can pull the code from the base class constructor, and put it in an Init method which is called from within the derived class's constructor. Within that method, the virtual dispatch will function as desired.

You might also consider lazy initialization... store the input parameters, and only perform the calculations the first time the functor is executed. After that, just return the cached results. This requires a not-insignifiant amount of additional overhead, but has the benefit of being completely safe, no matter what your final inheritance diagram ends up looking like. Stateful functors have their own issues, though, depending on how you plan on using this class.

灰色世界里的红玫瑰 2024-12-01 11:43:44

要么在内部class Partition内有一个virtual void RunAlgorithm()的方法体,要么将其声明为纯virtual

virtual void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput) = 0;

有一种特殊情况, ,当声明该类或其子对象时,任何虚函数都不能保持未实现状态(即使未使用该函数)。

Either have a method body for virtual void RunAlgorithm() inside the inner class Partition or declare it as pure virtual:

virtual void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput) = 0;

There is a special case that, any virtual function can never remain unimplemented when that class or its child objects are declared (even though the function as such not used).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文