1.整体架构:
Ui (.h/.cpp) : 用户交互层
AppLogic (h/.cpp) : 应用逻辑层
DataAccess (h/.cpp) : 数据处理
main (.cpp) : 主函数入口
template<class T, int Maxsize>
SeqList{
private:
T data[MaxSize];
int length;
T* IndexP[MaxSize];//按商品代号排序的指针数组
T* NameP[MaxSize];//按商品名称排序的指针数组
T* PriceP[MaxSize];//按商品价格排序的指针数组
int pLength;
//逻辑删除时,pLength--,因为data[MaxSize]数组中没有 物理删除数据,所以length永远都不会减小!!!
public:
SeqList();
SeqList(T a[], int n);
int ListLength();
T Get(int pos);
int Locate(T item);
void PrintSeqList();
void Insert(int i, T item);
T Delete(int i);
T DeleteMin();
void DeleteAllX(T x);
void DeleteS2T(T s, T t);
void DeleteRepeate();
void partSort();
void QuickSort(int l, int r);
void Merge(SeqList<T, MaxSize> &seqList2);
int GetpLength();//有效元素个数
T IndexPAt(int i);//商品代号指针数组在下标为i上的元素
void Append(T &t);//增
void DeleteByIndex(int index);//按商品代号删除
void DeleteByName(string name);//按商品名称删除
void DeleteByPrice(double price);//按商品价格删除
void UpdateByIndex(int index);//按商品代号更新
void SearchByIndex(int index);//按商品代号二分查找
void SearchByName(string name);//按商品名称二分查找
void SearchByPrice(double price);//按商品价格二分查找
void PrintByIndexOrder();//按商品代号遍历打印
void PrintByNameOrder();//按商品名称遍历打印
void PrintByPriceOrder();//按商品价格遍历打印
};
2.实例化时控制好MaxSize,防止栈溢出
模板类SeqList在实例化时,因为是线性表结构,内部是用数组int data[MaxSize]实现的,所以在实例化的时候是从栈中申请空间,故MaxSize不能设得太大,否则会导致栈溢出。
3.SeqList中设置多个指针数组,实现多重排序
T* IndexP[MaxSize];//按商品代号排序的指针数组
T* NameP[MaxSize];//按商品名称排序的指针数组
T* PriceP[MaxSize];//按商品价格排序的指针数组
class Goods {
private:
int Index;
string Name;
double Price;
int Count;
int PosOfIndexP;
int PosOfNameP;
int PosOfPriceP;
public:
Goods();
Goods(int index, string name, double price, int count);
int GetIndex();
string GetName();
double GetPrice();
int GetCount();
int GetPosOfIndexP();
int GetPosOfNameP();
int GetPosOfPriceP();
void SetPosOfIndexP(int pos);
void SetPosOfNameP(int pos);
void SetPosOfPriceP(int pos);
void SetPrice(double price);
void SetCount(int count);
Goods& operator=(const Goods &g);
bool operator>(const Goods &g);
bool operator<(const Goods &g);
bool operator>=(const Goods &g);
bool operator<=(const Goods &g);
bool operator==(const Goods &g);
bool operator!=(const Goods &g);
friend ostream& operator<<(ostream &o, Goods &g);
friend istream& operator>>(istream &i, Goods &g);
};
4.在Goods类中设置在多重排序指针数组中的位置的变量,以实现逻辑删除;并使得删除时pLength–,而length保持永远不会减少,真正实现逻辑删除
int PosOfIndexP;//当前Goods g在IndexP指针数组中的位置
int PosOfNameP;//当前Goods g在NameP指针数组中的位置
int PosOfPriceP;//当前Goods g在PriceP指针数组中的位置
5.比较操作符的重载,赋值运算符的重载,输入/输出操作符的重载
6.增删改查的实现代码
//构造函数
//无参构造函数
template<class T, int MaxSize>
SeqList<T, MaxSize>::SeqList() {
length = 0;
pLength = 0;
}
//有参构造函数
template<class T, int MaxSize>
SeqList<T, MaxSize>::SeqList(T a[], int n) {
if (n > MaxSize) {
cerr << "参数非法";
exit(1);
}
length = 0;
pLength = 0;
for (int i = 0; i < n; i++) {
//*1,*2两步,避免野指针!!!
data[length] = a[i];//*1
int j;
//找到正确位置,后面的指针向后移一位,本指针插入正确位置
for (j = 0; j < pLength; j++) {
if (a[i].GetIndex() < IndexP[j]->GetIndex()) {
for (int k = pLength; k > j; k--) {
IndexP[k] = IndexP[k - 1];
IndexP[k]->SetPosOfIndexP(IndexP[k]->GetPosOfIndexP() + 1);
}
IndexP[j] = &data[length];//*2
IndexP[j]->SetPosOfIndexP(j);
break;
}
}
//没有“正确”位置,本指针插入尾部
if (j == pLength) {
IndexP[j] = &data[length];
IndexP[j]->SetPosOfIndexP(j);
}
for (j = 0; j < pLength; j++) {
if (strcmp(a[i].GetName().c_str(), NameP[j]->GetName().c_str()) < 0) {
for (int k = pLength; k > j; k--) {
NameP[k] = NameP[k - 1];
NameP[k]->SetPosOfNameP(NameP[k]->GetPosOfNameP() + 1);
}
NameP[j] = &data[length];
NameP[j]->SetPosOfNameP(j);
break;
}
}
if (j == pLength) {
NameP[j] = &data[length];
NameP[j]->SetPosOfNameP(j);
}
for (j = 0; j < pLength; j++) {
if (a[i].GetPrice() < PriceP[j]->GetPrice()) {
for (int k = pLength; k > j; k--) {
PriceP[k] = PriceP[k - 1];
PriceP[k]->SetPosOfPriceP(PriceP[k]->GetPosOfPriceP() + 1);
}
PriceP[j] = &data[length];//*2
PriceP[j]->SetPosOfPriceP(j);
break;
}
}
if (j == pLength) {
PriceP[j] = &data[length];
PriceP[j]->SetPosOfPriceP(j);
}
length++;
pLength++;
}
}
//求有效元素的个数
template<class T, int MaxSize>
int SeqList<T, MaxSize>::GetpLength() {
return pLength;
}
//返回商品代号指针数组IndexP下标为i的元素
template<class T, int MaxSize>
T SeqList<T, MaxSize>::IndexPAt(int i) {
return *IndexP[i];
}
//增删改查
//每次都把T添加到data数组尾部,并更新各个指针数组
template<class T, int MaxSize>
void SeqList<T, MaxSize>::Append(T &t) {
data[length] = t;//*1.先将t复制给data尾部
int i;
for (i = 0; i < pLength; i++) {
/*!!!!通过*1和*2两步将指针指向data数组中的元素,避免了因指向t的地址,而当临时变量t销毁时,
本次插入的指针就乱指的情况!!!!*/
if (t.GetIndex() < IndexP[i]->GetIndex()) {
for (int j = pLength; j > i; j--) {
IndexP[j] = IndexP[j - 1];
IndexP[j]->SetPosOfIndexP(IndexP[j]->GetPosOfIndexP() + 1);
}
IndexP[i] = &data[length];//*2.将本次插入的指针指向data数组的元素
IndexP[i]->SetPosOfIndexP(i);
break;
}
}
if (i == pLength) {
IndexP[i] = &data[length];
IndexP[i]->SetPosOfIndexP(i);
}
for (i = 0; i < pLength; i++) {
if (strcmp(t.GetName().c_str(),NameP[i]->GetName().c_str()) < 0) {
for (int j = pLength; j > i; j--) {
NameP[j] = NameP[j - 1];
NameP[j]->SetPosOfNameP(NameP[j]->GetPosOfNameP() + 1);
}
NameP[i] = &data[length];
NameP[i]->SetPosOfNameP(i);
break;
}
}
if (i == pLength) {
NameP[i] = &data[length];
NameP[i]->SetPosOfNameP(i);
}
for (i = 0; i < pLength; i++) {
if (t.GetPrice() < PriceP[i]->GetPrice()) {
for (int j = pLength; j > i; j--) {
PriceP[j] = PriceP[j - 1];
PriceP[j]->SetPosOfPriceP(PriceP[j]->GetPosOfPriceP() + 1);
}
PriceP[i] = &data[length];
PriceP[i]->SetPosOfPriceP(i);
break;
}
}
if (i == pLength) {
PriceP[i] = &data[length];
PriceP[i]->SetPosOfPriceP(i);
}
length++;
pLength++;
}
//按商品代号删除,用二分查找提高效率
template<class T, int MaxSize>
void SeqList<T, MaxSize>::DeleteByIndex(int index) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (IndexP[mid]->GetIndex() < index)
l = mid + 1;
else if (IndexP[mid]->GetIndex() > index)
r = mid - 1;
else {
/*
因为用了多个数组指针,所以进行物理删除时(即Delete(???))会使得数组指针中的指针发生偏移,
故用逻辑删除(即只删除指针数组中的指针,而data数组中的数据不删除)
进行多次逻辑删除,先删除NameP和PriceP数组中的指针,再删除IndexP数组中的指针
*/
for (int i = IndexP[mid]->GetPosOfNameP(); i < pLength - 1; i++) {
NameP[i] = NameP[i + 1];
NameP[i]->SetPosOfNameP(NameP[i]->GetPosOfNameP() - 1);
}
NameP[pLength - 1] = NULL;
for (int i = IndexP[mid]->GetPosOfPriceP(); i < pLength - 1; i++) {
PriceP[i] = PriceP[i + 1];
PriceP[i]->SetPosOfPriceP(PriceP[i]->GetPosOfPriceP() - 1);
}
PriceP[pLength - 1] = NULL;
for (int i = mid; i < pLength - 1; i++) {
IndexP[i] = IndexP[i + 1];
IndexP[i]->SetPosOfIndexP(IndexP[i]->GetPosOfIndexP() - 1);
}
IndexP[pLength - 1] = NULL;
pLength--;
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品名称删除,用二分查找提高效率
template<class T, int MaxSize>
void SeqList<T, MaxSize>::DeleteByName(string name) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (strcmp(NameP[mid]->GetName().c_str(), name.c_str()) < 0)
l = mid + 1;
else if (strcmp(NameP[mid]->GetName().c_str(), name.c_str()) > 0)
r = mid - 1;
else {
/*
因为用了多个数组指针,所以进行物理删除时(即Delete(???))会使得数组指针中的指针发生偏移,
故用逻辑删除(即只删除指针数组中的指针,而data数组中的数据不删除)
进行多次逻辑删除,先删除IndexP和PriceP数组中的指针,再删除NameP数组中的指针
*/
for (int i = NameP[mid]->GetPosOfIndexP(); i < pLength - 1; i++) {
IndexP[i] = IndexP[i + 1];
IndexP[i]->SetPosOfIndexP(IndexP[i]->GetPosOfIndexP() - 1);
}
IndexP[pLength - 1] = NULL;
for (int i = NameP[mid]->GetPosOfPriceP(); i < pLength - 1; i++) {
PriceP[i] = PriceP[i + 1];
PriceP[i]->SetPosOfPriceP(PriceP[i]->GetPosOfPriceP() - 1);
}
PriceP[pLength - 1] = NULL;
for (int i = mid; i < pLength- 1; i++) {
NameP[i] = NameP[i + 1];
NameP[i]->SetPosOfNameP(NameP[i]->GetPosOfNameP() - 1);
}
NameP[pLength - 1] = NULL;
pLength--;
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品价格删除,用二分查找提高效率
template<class T, int MaxSize>
void SeqList<T, MaxSize>::DeleteByPrice(double price) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (PriceP[mid]->GetPrice() < price)
l = mid + 1;
else if (PriceP[mid]->GetPrice() > price)
r = mid - 1;
else {
/*
因为用了多个数组指针,所以进行物理删除时(即Delete(???))会使得数组指针中的指针发生偏移,
故用逻辑删除(即只删除指针数组中的指针,而data数组中的数据不删除)
进行多次逻辑删除,先删除IndexP和NameP数组中的指针,再删除PriceP数组中的指针
*/
for (int i = PriceP[mid]->GetPosOfIndexP(); i < pLength - 1; i++) {
IndexP[i] = IndexP[i + 1];
IndexP[i]->SetPosOfIndexP(IndexP[i]->GetPosOfIndexP() - 1);
}
IndexP[pLength - 1] = NULL;
for (int i = PriceP[mid]->GetPosOfNameP(); i < pLength - 1; i++) {
NameP[i] = NameP[i + 1];
NameP[i]->SetPosOfNameP(NameP[i]->GetPosOfNameP() - 1);
}
NameP[pLength - 1] = NULL;
for (int i = mid; i < pLength - 1; i++) {
PriceP[i] = PriceP[i + 1];
PriceP[i]->SetPosOfPriceP(PriceP[i]->GetPosOfPriceP() - 1);
}
PriceP[pLength - 1] = NULL;
pLength--;
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品代号更新
template<class T, int MaxSize>
void SeqList<T, MaxSize>::UpdateByIndex(int index) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (IndexP[mid]->GetIndex() < index)
l = mid + 1;
else if (IndexP[mid]->GetIndex() > index)
r = mid - 1;
else {
double price;
int count;
cout << "请输入更新后的价格:";
cin >> price;
cout << "请输入更新后的库存:";
cin >> count;
string name = IndexP[mid]->GetName();
T t(index, name, price, count);
//更新价格时会导致新的价格排序,采用逻辑删除此结点,再添加一个新的结点来实现,这样比较简单!!!
this->DeleteByIndex(index);
this->Append(t);
break;
}
}
//不存在,则增加一条记录
if (l > r) {
string name;
double price;
int count;
cout << "该商品不存在,输入其信息后会自动添加商品记录:" << endl;
cout << "请输入商品名称:";
cin >> name;
cout << "请输入商品价格:";
cin >> price;
cout << "请输入商品库存:";
cin >> count;
Goods g(index, name, price, count);
this->Append(g);
}
}
//按商品代号二分查找
template<class T, int MaxSize>
void SeqList<T, MaxSize>::SearchByIndex(int index) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (IndexP[mid]->GetIndex() < index)
l = mid + 1;
else if (IndexP[mid]->GetIndex() > index)
r = mid - 1;
else {
cout << *IndexP[mid];
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品名称二分查找
template<class T, int MaxSize>
void SeqList<T, MaxSize>::SearchByName(string name) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (strcmp(NameP[mid]->GetName().c_str(),name.c_str()) < 0)
l = mid + 1;
else if (strcmp(NameP[mid]->GetName().c_str(), name.c_str()) > 0)
r = mid - 1;
else {
cout << *NameP[mid];
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品价格二分查找
template<class T, int MaxSize>
void SeqList<T, MaxSize>::SearchByPrice(double price) {
int l = 0, r = pLength - 1, mid;
while (l <= r) {
mid = (l + r) / 2;
if (PriceP[mid]->GetPrice() < price)
l = mid + 1;
else if (PriceP[mid]->GetPrice() > price)
r = mid - 1;
else {
cout << *PriceP[mid];
break;
}
}
if (l > r)
cout << "此商品不存在!" << endl;
}
//按商品代号打印
template<class T, int MaxSize>
void SeqList<T, MaxSize>::PrintByIndexOrder() {
for (int i = 0; i < pLength; i++) {
cout << *IndexP[i];
}
}
//按商品名称打印
template<class T, int MaxSize>
void SeqList<T, MaxSize>::PrintByNameOrder() {
for (int i = 0; i < pLength; i++) {
cout << *NameP[i];
}
}
//按商品价格打印
template<class T, int MaxSize>
void SeqList<T, MaxSize>::PrintByPriceOrder() {
for (int i = 0; i < pLength; i++) {
cout << *PriceP[i];
}
}
7.文件读写,写入时,(注1)先第一行写一条pLength数据,记录有效元素个数,并按有效元素个数,(注2)按照IndexP指针数组,用IndexPAt(int i)操作,按顺序将有效的pLength个数据写入文件
void LoadData(SeqList<Goods, 10000> &myList, char* fileName) {
int index;
string name;
double price;
int count;
ifstream in(fileName);
if (!in.is_open()) {
cout << "Error opening file!";
exit(1);
}
int num;
in >> num;
if (num == 0) {
in.close();
return;
}
while (!in.eof()) {
in >> index >> name >> price >> count;
Goods g(index, name, price, count);
myList.Append(g);
}
in.close();
}
void SaveData(SeqList<Goods, 10000> &myList, char* fileName) {
ofstream out(fileName);
if (!out.is_open()) {
cout << "Error opening file!";
exit(1);
}
int n = myList.GetpLength();
out << n << endl;//第一行写总共有多少个有效数据
for (int i = 0; i < n - 1; i++) {
out << myList.IndexPAt(i).GetIndex() << " " << myList.IndexPAt(i).GetName() << " " <<
myList.IndexPAt(i).GetPrice() << " " << myList.IndexPAt(i).GetCount() << endl;
}
//最后一行特殊化处理,不能写入换行!!!
out << myList.IndexPAt(n - 1).GetIndex() << " " << myList.IndexPAt(n - 1).GetName() << " " <<
myList.IndexPAt(n - 1).GetPrice() << " " << myList.IndexPAt(n - 1).GetCount();
out.close();
}