重载基类非虚函数中使用的虚函数
overloading a virtual function that is used in a base classes non-virtual function
嘿,所以我试图建立类的以下成员函子ConcavePolygon,我得到链接器外部符号错误出于某种原因:
未解析的外部符号"public: virtual void __thiscall "sf::ConcavePolygon::Partition::RunAlgorithm(class 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
看起来在您的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
方法中。在该方法中,虚拟调度将按所需方式运行。
你也可以考虑延迟初始化…存储输入参数,并且只在第一次执行函子时执行计算。之后,只需返回缓存的结果。这需要相当多的额外开销,但具有完全安全的好处,无论最终的继承图是什么样子。但是,有状态函数也有自己的问题,这取决于您计划如何使用该类。
在class Partition
内部有virtual void RunAlgorithm()
的方法体,或者将其声明为纯virtual
:
virtual void RunAlgorithm(TPPLPoly& Poly, std::list<TPPLPoly>& PartitionOutput) = 0;
有一个特殊的情况,任何虚函数都不能在该类或其子对象声明时保持未实现状态(即使函数本身没有被使用)。
- 基类中的函数名称解析
- 如何通过派生类函数更改基类中的向量
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如果基类包含双指针成员,则派生类的构造函数
- C++ 继承:将子类传递给需要基类的函数并获取子类行为
- 继承和友元函数,从基类访问受保护的成员
- 使用子类覆盖基类中定义的函数
- 如何基于模板化类的基类专门化成员函数
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 使用基类指针调用基类的值构造函数的语法是什么?
- C++重载函数,一个采用基类的参数,另一个采用派生类的参数
- 如何在基类指针向量的元素上应用重载的多态函数
- 如何在不使用指针的情况下将派生类的对象作为参数传递给基类中的函数?
- 被覆盖的函数不会反映在基类中,这是正常行为吗?
- 在初始化列表之外手动调用基类的构造函数
- 重写打印函数而不是覆盖基类
- C++17 使用驱动类常量作为基类构造函数的参数来初始化基类构造函数
- C++虚拟函数:基类函数是调用的,而不是派生的
- 虚拟函数-基类指针