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

时间:2011-07-16 03:34:39

标签: c++ polymorphism virtual linker-errors functor

嘿所以我正在尝试构建类 ConcavePolygon 的以下成员Functors,并且由于某种原因我得到了Linker External符号错误:

  

未解析的外部符号“public:virtual void __thiscall   sf :: ConcavePolygon :: Partition :: RunAlgorithm(类TPPLPoly&,类   std :: list> &安培)“

我的目标是简单地创建一个包含SFML(lib)可以理解的图形数据的类,并将其用于分区或三角化图形数据(Polygon(s))

而不是用非常相似的代码编写两个大函数;一个到Triangulate,一个做我称之为Convexulating,我决定尝试使用仿函数,并使基础算符 分区,后代Triangulate和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

2 个答案:

答案 0 :(得分:1)

在内部virtual void RunAlgorithm()内有class Partition的方法正文或将其声明为纯virtual

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

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

答案 1 :(得分:1)

看起来在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

在派生类构造函数的输出之前看到偷偷摸摸的析构函数调用?这是被摧毁的临时对象。在该临时对象的构造函数中,它尝试调用您未实现的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方法中。在该方法中,虚拟调度将按需运行。

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