作为C ++的新手,我很难理解我遇到的链接问题。以下是让我头疼的文件:
#pragma once
#include <string>
#include "finite_automaton.h"
template<typename RL>
class RegularLanguage {
public:
bool empty();
RegularLanguage<RL> minimize();
RegularLanguage<RL> complement();
RegularLanguage<RL> intersectionWith(RegularLanguage<RL> other);
RegularLanguage<RL> unionWith(RegularLanguage<RL> other);
RegularLanguage<RL> concatenate(RegularLanguage<RL> other);
RegularLanguage<RL> kleeneStar();
/*
* Returns a regular expression that describes the language of this automaton.
*/
std::string string();
bool disjoint(RegularLanguage<RL> other) {
return intersectionWith(other).empty();
}
bool containedIn(RegularLanguage<RL> super) {
return intersectionWith(super.complement()).empty();
}
bool contains(RegularLanguage<RL> sub) {
return complement().intersectionWith(sub).empty();
}
bool equals(RegularLanguage<RL> other) {
return contains(other) && containedIn(other);
}
};
编译项目时,在链接阶段会出现以下错误:
undefined reference to `RegularLanguage<FiniteAutomaton>::complement()'
undefined reference to `RegularLanguage<FiniteAutomaton>::intersectionWith(RegularLanguage<FiniteAutomaton>)'
undefined reference to `RegularLanguage<FiniteAutomaton>::empty()'
RegularLanguage<RL>::containedIn(..)
和RegularLanguage<RL>::contains(..)
都适用。我究竟做错了什么?我确实得到了与实现此模板类的类有关的一些相关错误,但为了避免不必要地发布过多的代码,我将它们省略了。
最佳答案
因此,总结一下您要做什么,您有一个类模板:
template<typename RL> class RegularLanguage {...};
某些方法已实现,而其他方法仅声明但未实现。然后尝试从中派生,并在派生类中实现那些在
RegularLanguage
中未实现的其他方法:class FiniteAutomaton : public RegularLanguage<FiniteAutomaton>
您似乎正在混淆C ++中的两个概念:继承(具体来说是多态)和模板。
您的假设是,通过从
RegularLanguage
继承,您可以在派生类中实现缺少的东西。那不是模板的工作方式,但是多态确实可以这样工作。您需要:在类模板
RegularLanguage
中完全实现所有方法,然后从中派生FiniteAutomaton
。使用(可能是纯净的)
RegularLanguage
方法将virtual
作为抽象基类(而不是类模板),然后从中派生。您想要在FiniteAutomaton
中实现的方法可以。这些可能是纯粹的,也可能不是。您不想在基类中实现的方法应声明为纯(=0
),然后在派生类中实现。在我看来,使用#2可以更好地完成您真正想做的事情,因此,下面是一个不完整的示例:
class RegularLanguage {
public:
virtual RegularLanguage* Clone() = 0; // Creates a copy of this object
virtual RegularLanguage& complement() = 0; // pure virtual must be implemented in derived class
virtual bool containedIn(RegularLanguage& super) // non-pure, does not have to be implemented in derived
{
return intersectionWith(super.complement()).empty();
}
virtual ~RegularLanguage() {}
};
class FiniteAutomaton
:
public RegularLanguage
{
public:
RegularLanguage* Clone()
{
RegularLanguage* clone = new FiniteAutomaton (*this);
return clone;
}
RegularLanguage* complement()
{
RegularLanguage* comp = this->Clone();
comp->SetSomeStuff();
return comp;
}
};
我上面没有提到很多隐藏的细节。例如,
complement
的返回类型现在是RegularLanguage
指针,而不是RegularLanguage
值。这是必需的1,以避免所谓的Slicing Problem破坏多态性。另外,我在这里放弃了模板的使用,因为当我实现了模板之后,显然并不需要模板。但是,模板的使用和多态性并不完全相互排斥。基类可以使用模板,实际上基类可以是类模板,而仍然是抽象基类。但是派生类必须从该基类模板的具体实例派生。这里的事情变得有些复杂。请注意,不要将所有东西都看成是钉子,并被那把锤子带走。
另外,我之前没有在基类中实现虚拟析构函数(现在已解决),但是您必须2。请记住这一点:如果要派生一个类,则几乎在每种情况下都应具有一个虚拟析构函数。
另外,我在基类中添加了一个
Clone
方法作为纯virtual
。这是对以下建议的回应:complement()
不应返回该对象的实例,而应返回一个新实例,该实例是该实例的补充。在多态层次结构中,当我们复制对象的副本时,几乎总是需要通过基类指针进行复制,因此在这种设计中通常存在Clone()
类型的方法。1“这是必需的[通过指针传递]:实际上,您也可以返回一个引用。如果可以,请返回一个引用,但是这里我们需要返回一个指针。(实际上,我们应该返回智能指针,但这是一个切线)
2“您必须[实现虚拟析构函数]:从技术上讲,如果您打算通过基类指针
delete
该对象,则需要具有虚拟析构函数。在我的职业生涯中,我从未见过这样的实例:不应在多态层次结构的基类中实现虚拟析构函数,即使您没有通过基类指针delete
的计划,也应始终这样做。关于c++ - 类内的C++链接,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21342008/