C++;类内联系 对于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); } }; #pragma一次 #包括 #包括“有限自动机.h” 模板 类正则语言{ 公众: bool empty(); 正则语言最小化(); 正规语言补语(); 正规语言与(正规语言其他)交叉; 正规语言联盟(正规语言其他); RegularLanguage连接(RegularLanguage其他); 正则语言kleenstar(); /* *返回描述此自动机语言的正则表达式。 */ std::string(); 布尔不相交(常规语言其他){ 返回intersectionWith(other.empty(); } bool containedIn(正规语言超级版){ 返回intersectionWith(super.complete()).empty(); } bool包含(常规语言子系统){ 返回补码().intersectionWith(sub.empty(); } 布尔等于(常规语言其他){ 退货包含(其他)和包含在(其他)中; } };

C++;类内联系 对于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); } }; #pragma一次 #包括 #包括“有限自动机.h” 模板 类正则语言{ 公众: bool empty(); 正则语言最小化(); 正规语言补语(); 正规语言与(正规语言其他)交叉; 正规语言联盟(正规语言其他); RegularLanguage连接(RegularLanguage其他); 正则语言kleenstar(); /* *返回描述此自动机语言的正则表达式。 */ std::string(); 布尔不相交(常规语言其他){ 返回intersectionWith(other.empty(); } bool containedIn(正规语言超级版){ 返回intersectionWith(super.complete()).empty(); } bool包含(常规语言子系统){ 返回补码().intersectionWith(sub.empty(); } 布尔等于(常规语言其他){ 退货包含(其他)和包含在(其他)中; } };,c++,linker,C++,Linker,编译项目时,在链接阶段会出现以下错误: undefined reference to `RegularLanguage<FiniteAutomaton>::complement()' undefined reference to `RegularLanguage<FiniteAutomaton>::intersectionWith(RegularLanguage<FiniteAutomaton>)' undefined reference to `Regula

编译项目时,在链接阶段会出现以下错误:

undefined reference to `RegularLanguage<FiniteAutomaton>::complement()'
undefined reference to `RegularLanguage<FiniteAutomaton>::intersectionWith(RegularLanguage<FiniteAutomaton>)'
undefined reference to `RegularLanguage<FiniteAutomaton>::empty()'
RegularLanguage::complete()的未定义引用
对“RegularLanguage::intersectionWith(RegularLanguage)”的未定义引用
对“RegularLanguage::empty()”的未定义引用
用于
RegularLanguage::containedIn(..)
RegularLanguage::contains(..)


我做错了什么?我确实收到了一些与实现此模板类的类相关的错误,但为了不发布不必要的代码,我忽略了这些错误。

您需要将
bool empty()
(以及其他未在基类中实现的类)声明为基类中的纯虚拟类:

virtual bool empty() = 0;
然后在子类中重写/实现它们,如下所示:

virtual bool empty(){
    ...
}

您需要将
bool empty()
(以及其他未在基类中实现的)声明为基类中的纯虚拟:

virtual bool empty() = 0;
然后在子类中重写/实现它们,如下所示:

virtual bool empty(){
    ...
}

因此,为了总结您要做的事情,您有一个类模板:

template<typename RL> class RegularLanguage {...};
在C++中,您似乎混淆了两个概念:继承(特别是多态性)和模板

您的假设是,通过从
RegularLanguage
继承,可以实现派生类中缺少的内容。模板不是这样工作的,但多态性确实是这样工作的。您需要:

  • 完全实现类模板
    RegularLanguage
    中的所有方法,然后从中派生
    FiniteAutomaton
  • 使用(可能是纯的)
    virtual
    方法将
    RegularLanguage
    作为抽象基类,而不是类模板,并从中派生。您希望在
    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;
      }
    };
    
    有很多细节我没有提到。首先,
    补码
    的返回类型现在是
    RegularLanguage
    指针,而不是
    RegularLanguage
    的值。这是必需的,1是为了避免所谓的破坏多态性

    另一方面,我在这里放弃了模板的使用,因为我实现了这一点,显然不需要它们。然而,模板和多态性的使用并不是完全相互排斥的。基类可以使用模板,实际上基类可以是类模板,但仍然是抽象基类但是派生类必须派生自该基类模板的具体实例化。这里的情况有些复杂。只是要小心,不要把一切都看成钉子,否则会被锤子冲昏头脑

    另一方面,我以前没有在基类中实现虚拟析构函数(现在已经修复了),但是必须2。只要记住这一点:如果一个类打算从中派生,那么它在几乎所有情况下都应该有一个虚拟析构函数

    另一方面,我将一个
    Clone
    方法作为纯
    virtual
    添加到基类中。这是对以下建议的回应:
    complete()
    不应返回对象的此实例,而应返回作为此实例的补充的新实例。在多态层次结构中,当我们复制对象时,几乎总是需要通过基类指针进行复制,因此在这种设计中通常会出现
    Clone()
    type方法


    1“这是必需的[通过指针传递]:实际上,您也可以返回引用。如果可以,请返回引用,但这里我们需要返回指针。(实际上,我们应该返回智能指针,但这是切线。)


    2“您必须[实现一个虚拟析构函数]:从技术上讲,如果您打算通过基类指针
    删除对象,您需要一个虚拟析构函数。在我的职业生涯中,我从未见过一个我不能或不应该在多态层次结构的基类中实现虚拟析构函数的实例。说您应该始终这样做几乎是正确的,即使您没有计划通过基类指针
    删除

    因此,为了总结您要做的事情,您有一个类模板:

    template<typename RL> class RegularLanguage {...};
    
    在C++中,您似乎混淆了两个概念:继承(特别是多态性)和模板

    您的假设是,通过从
    RegularLanguage
    继承,可以实现派生类中缺少的内容。不是这样的