问题解决:继承抽象类时出现error LINK2001 无法解析的外部符号

平时在编程过程中创建一个新的类时,已习惯遵循以下规则:

1. 为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块;
2. 用 #include <filename.h> 格式来引用标准库的头文件(编译器将从标准库目录开始搜索);
3. 用 #include "filename.h" 格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索);
4. 该类的头文件中只存放声明而不存放定义,而在cpp文件中对类的成员函数进行定义。这是因为在C++语法中,类的成员函数可以在声明的同时被定义,并且自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。

但在一次编程练习中使用了一个抽象类List,然后通过一个AList类说明来继承抽象类List并实现List的所有成员函数。AList.h给出函数声明,AList.cpp给出具体实现,在编译时出现以下问题:

这里写图片描述

这个问题算是最常见的问题,网上的解决方法也是相当多,但这些解决方法还是一时无法解决我的问题,后来是看到了论坛里一位同行提出的一个方法才得以解决。

以下是实现线性表的代码:

这里依据一个线性表的一组操作来定义对象的抽象数据类型ADT,使用C++中一个名为List的抽象类表示法正式定义线性表ADT,这个抽象类的所有成员函数都被声明为“纯虚的”(pure virtual),在函数声明的最后均有“=0”的符号。

// List的抽象类
template <class Elem> class List
{
public:
    // 清除操作
    virtual void clear() = 0;
    // 插入Elem类型的数据
    virtual bool insert(const Elem&) = 0;
    // 附加Elem类型的数据
    virtual bool append(const Elem&) = 0;
    // 移除Elem类型的数据
    virtual bool remove(Elem&) = 0;
    // 设置栅栏在最左端
    virtual void setStart() = 0;
    // 设置栅栏在最右端
    virtual void setEnd() = 0;
    // 将栅栏往左移一位
    virtual void prev() = 0;
    // 将栅栏往右移一位
    virtual void next() = 0;
    // 返回线性表左边部分的长度
    virtual int leftLength() const = 0;
    // 返回线性表右边部分的长度
    virtual int rightLength() const = 0;
    // 若值pos存在,以pos为当前位置并输出ture,否则不操作并输出false
    virtual bool setPos(int pos) = 0;
    // 在栅栏右方寻找第一次出现的特定值,找到ture否则false
    virtual bool getValue(Elem&) const = 0;
    // 打印线性表的值
    virtual void print() const = 0;
};

这里使用AList继承上面的抽象类List,一开始在.h文件中只给出成员函数的声明,而把函数的定义放在.cpp文件中。

#include "list.h"
#include <iostream>
template < class Elem >
class AList : public List<Elem>
{
private:
    int maxSize;        // 线性表的最大尺寸
    int listSize;       // 当前线性表的尺寸
    int fence;          // 当前元素的位置
    Elem* listArray;    // 装载列表元素的数组
    //int defaultSize = 10;  // 默认线性表最大长度

public:
    AList(int size = 10)    // 构造函数
    { 
        maxSize = size;
        listSize = fence = 0;
        listArray = new Elem[maxSize];
    }
    ~AList() { delete[] listArray; } // 析构函数
    void clear();
    bool insert(const Elem& item); // 在线性表中插入一个数
    bool append(const Elem& item); // 在线性表的末尾插入一个数
    bool remove(Elem& it);       // 移除线性表中的一个数
    void setStart();
    void setEnd();
    void prev();
    void next(); 
    int leftLength()const; 
    int rightLength()const; 
    bool setPos(int Pos);
    bool getValue(Elem& it) const;
    void print() const;
};

最后给出各个函数的定义:

#include "AList.h"
template < class Elem >
void AList<Elem>::clear()
{                    
    delete[] listArray;             // 删除整个顺序表
    listSize = fence = 0;           // 重置数组尺寸和栅栏
    listArray = new Elem[maxSize];  // 重新为新的顺序表开辟内存空间
}

template < class Elem >
bool AList<Elem>::insert(const Elem& item)
{
    if (listSize == maxSize) return false;
    for (int i = listSize; i > fence; i--)
        listArray[i] = listArray[i - 1];
    listArray[fence] = item;
    listSize++;
    return true;
}

template < class Elem >
bool AList<Elem>::append(const Elem& item)
{
    if (listSize == maxSize) return false;
    listArray[listSize++] = item;
    return true;
}

template < class Elem >
bool AList<Elem>::remove(Elem& it)
{
    if (rightLength == 0) return false;
    it = listArray[fence];
    for (int i = fence; i < listSize - 1; i++)
        listArray[i] = listArray[i + 1];
    listSize--;
    return true;
}

template < class Elem >
void AList<Elem>::setStart(){ fence = 0; }

template < class Elem >
void AList<Elem>::setEnd(){ fence = listSize; }

template < class Elem >
void AList<Elem>::prev(){ if (fence != 0) fence--; }

template < class Elem >
void AList<Elem>::next(){ if (fence <= listSize) fence++; }

template < class Elem >
int AList<Elem>::leftLength()const{ return fence; }

template < class Elem >
int AList<Elem>::rightLength()const{ return listSize - fence; }

template < class Elem >
bool AList<Elem>::setPos(int Pos)
{
    if (Pos > 0 && Pos < listSize) fence = Pos;
    return (Pos > 0) && (Pos < listSize);
}

template < class Elem >
bool AList<Elem>::getValue(Elem& it) const
{
    if (rightLength() == 0) return false;
    else 
    {
        it = listArray[fence]; 
        return true;
    }
}

template < class Elem >
void AList<Elem>::print() const
{
    int temp = 0;
    std::cout << "< ";
    while (temp < fence) std::cout << listArray[temp++] << " ";
    // 到栅栏处
    std::cout << "| ";
    while (temp < listSize) std::cout << listArray[temp++] << " ";
    std::cout << ">\n";
}

经查明原因,编译器在编译.h和.cpp文件时会按照编译单元来编译。在C++中一个编译单元是一个.h和一个.cpp文件,而在抽象类中就不同了,因为不能确定具体类型,所以就会导致编译单元不能被确定,所以在写继承抽象类时,需要将类的声明和实现写在同一个文件中,这样才不致于出错。

因此无需在AList.cpp中写函数定义,直接写在Alist.h中即可,改过之后代码如下,该错误消失:

#include "list.h"
#include <iostream>
template < class Elem >
class AList : public List<Elem>
{
private:
    int maxSize;        // 线性表的最大尺寸
    int listSize;       // 当前线性表的尺寸
    int fence;          // 当前元素的位置
    Elem* listArray;    // 装载列表元素的数组
    //int defaultSize = 10;  // 默认线性表最大长度

public:
    AList(int size = 10)    // 构造函数
    { 
        maxSize = size;
        listSize = fence = 0;
        listArray = new Elem[maxSize];
    }
    ~AList() { delete[] listArray; } // 析构函数

    void clear()
    {
        delete[] listArray;             // 删除整个顺序表
        listSize = fence = 0;           // 重置数组尺寸和栅栏
        listArray = new Elem[maxSize];  // 重新为新的顺序表开辟内存空间
    }

    bool insert(const Elem& item) // 在线性表中插入一个数
    {
        if (listSize == maxSize) return false;
        for (int i = listSize; i > fence; i--)
            listArray[i] = listArray[i - 1];
        listArray[fence] = item;
        listSize++;
        return true;
    }

    bool append(const Elem& item) // 在线性表的末尾插入一个数
    {
        if (listSize == maxSize) return false;
        listArray[listSize++] = item;
        return true;
    }

    bool remove(Elem& it)       // 移除线性表中的一个数
    {
        if (rightLength() == 0) return false;
        it = listArray[fence];
        for (int i = fence; i < listSize - 1; i++)
            listArray[i] = listArray[i + 1];
        listSize--;
        return true;
    }

    void setStart(){ fence = 0; }
    void setEnd(){ fence = listSize; }
    void prev(){ if (fence != 0) fence--; }
    void next(){ if (fence <= listSize) fence++; }
    int leftLength()const{ return fence; }
    int rightLength()const{ return listSize - fence; }

    bool setPos(int Pos)
    {
        if (Pos > 0 && Pos < listSize) fence = Pos;
        return (Pos > 0) && (Pos < listSize);
    }

    bool getValue(Elem& it) const
    {
        if (rightLength() == 0) return false;
        else
        {
            it = listArray[fence];
            return true;
        }
    }
    void print() const
    {
        int temp = 0;
        std::cout << "< ";
        while (temp < fence) std::cout << listArray[temp++] << " ";
        // 到栅栏处
        std::cout << "| ";
        while (temp < listSize) std::cout << listArray[temp++] << " ";
        std::cout << ">\n";

    }
};

顺利生成一可操作的线性表:

这里写图片描述

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 继承(Inheritance):一种面向对象编程的机制,允许一个类从另一个类继承属性和方法。继承的类被称为子类或派生类,被继承的类被称为父类或基类。子类可以重写父类的方法或属性,也可以新增自己的方法或属性。 2. 抽象类(Abstract Class):一种不能被实例化的类,只能作为超类被其他类继承抽象类通常包含抽象方法或抽象属性,这些方法或属性只有声明,没有具体实现。子类必须实现它们才能被实例化。 3. 接口(Interface):一种定义了一组方法或属性的抽象类型。接口中的方法或属性没有具体实现,只有声明。类可以实现一个或多个接口,实现接口的类必须实现接口中所有的方法或属性。 4. 多态(Polymorphism):一种允许同一个方法在不同的对象上有不同行为的机制。多态可以通过继承、接口、重载等方式实现。它可以提高代码的复用性和灵活性。 5. 构造函数(Constructor):一种特殊的方法,用于创建对象初始化对象的属性。构造函数的名称与类名相同,没有返回值。 6. static:一种关键字,用于修饰类的属性和方法。静态属性和方法属于类,而不属于对象。静态属性和方法可以通过类名直接访问。 7. final:一种关键字,用于修饰类、方法和变量。final修饰的类不能被继承,修饰的方法不能被重写,修饰的变量只能被赋值一次。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值