平时在编程过程中创建一个新的类时,已习惯遵循以下规则:
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";
}
};
顺利生成一可操作的线性表: