overloading a virtual function that is used in a base classes non-virtual function
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
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.
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).
精彩评论