перегрузка виртуальной функции, которая используется в не виртуальной функции базовых классов - PullRequest
2 голосов
/ 16 июля 2011

Эй, я пытаюсь построить следующий член Функторы класса ConcavePolygon , и по какой-то причине я получаю внешнюю ошибку символа Linker External:

неразрешенный внешний символ "public: virtual void __thiscall" sf :: ConcavePolygon :: Partition :: RunAlgorithm (класс TPPLPoly &, класс std :: list> &) "

Моя цель состояла в том, чтобы просто создать класс, который содержит графические данные, понятные SFML (lib), и функционирует либо для разделения, либо для триангуляции графических данных (полигон (ы))

Вместо написания двух больших функций с ОЧЕНЬ похожим кодом; один для триангуляции, а другой для выполнения того, что я называю конвексирующим, я решил попытаться работать с функторами и создал базовый функтор Разделение с триангуляцией и выпуклостью потомков .

Базовый класс 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 ]

1 голос
/ 16 июля 2011

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

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

1 голос
/ 16 июля 2011

Либо иметь тело метода для virtual void RunAlgorithm() внутри внутреннего class Partition, либо объявить его чистым virtual:

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

Существует особый случай, когда любая виртуальная функция никогда не может оставаться не реализованной, когда этот класс или его дочерние объекты объявлены (даже если функция как таковая не используется).

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