昨天晚上做C++的大作業,沒錯,就是經典的學生成績管理系統。編譯通過,一切正常。運行時,系統中輸入其他命令(像insert啊,search啊,count啊blabala…)都能正常運行,但是輸入exit命令退出系統時就出錯。可以判斷,是最後退出時,我的鏈表類析構時出現了問題。
錯誤的提示信息有兩條如下:
_Mycont CXX0030: Error: expression cannot be evaluated
_Myfirstiter CXX0030: Error: expression cannot be evaluated
出錯的位置定位如下圖所示:
上網搜索後發現是讀寫二進制文件的鍋。我的鏈表節點裏含有string對象,而string對象裏包含指針。每次打開程序構造鏈表時會從二進制文件讀取上次保存的信息,那麼這個指針也會被讀進來,但是這個指針已經失效了,它指向的地址在這次運行時並沒有申請,所以析構對象時,去釋放它指向的地址是非法的。
鏈表節點的部分代碼:
class StuNode
{
friend class StuList;
std::string name_; //string對象
int number_;
std::string sex_; //string對象
...
StuNode *next_;
public:
....
}
寫入二進制文件的load()函數(在鏈表構造函數中調用):
void StuList::save()
{
FILE* pFile = fopen("save.dat", "wb");
for(StuNode *ptr = head_->next_;ptr!=NULL;ptr=ptr->next_)
{
fwrite(ptr,sizeof(StuNode),1,pFile);//這裏雖然將鏈表的指針也寫進了文件,但是讀文件重新建立鏈表時,指針將被新申請的地址替代,不會出錯
}
fclose(pFile);
}
這個問題我想到的解決方法:
1.使用字符串類型時用C風格的char *。
2.仍然使用string類,寫入二進制文件時不將每個節點作爲一個單元寫入,當遇到string對象時,只將字符串讀出,對象中的其他內容不讀。注意,這樣的話load()函數也要對應修改。
這裏採用第二種方法。
void StuList::save()//爲了防止保存指針,逐一保存數據域中各項
{
FILE* pFile = fopen("save.dat", "wb");
for(StuNode *ptr = head_->next_;ptr!=NULL;ptr=ptr->next_)
{
//遇到string對象保存字符串大小和字符串內容
std::string::size_type strsize=ptr->name_.size();
fwrite(&strsize,sizeof(strsize),1,pFile);
for(std::string::iterator it=ptr->name_.begin();
it!=ptr->name_.end();it++)
{
fwrite(&(*it),sizeof(char),1,pFile);
}
fwrite(&(ptr->number_),sizeof(int),1,pFile);
fwrite(&(ptr->birthday_),sizeof(Date),1,pFile);
... ...
}
fclose(pFile);
}
讀取string對象時先讀字符串大小,用一臨時char型變量ch逐個讀取字母,string::push_back(ch)。