2011-11-07 24 views
1

這些方法應該保存並加載它們關聯的對象的整體。當我通過gcc在Linux下編譯程序時,保存似乎工作,但在加載時發生段錯誤。當我通過Visual Studio編譯器在Windows下編譯它時,它的工作方式就像是一場夢。我不確定這些差異是什麼,但我有一種預感,它涉及一些海灣合作委員會的古怪事物。GCC下的對象加載段錯誤

兩種方法:

void User::SaveToFile() 
{ 
    ofstream outFile; 
    string datafile_name = username + "_data"; 
    outFile.open(datafile_name.c_str(), ios::binary); 
    outFile.write((char*)this, sizeof(*this)); 
} 
void User::LoadFromFile(string filename) 
{ 
    ifstream inFile; 
    inFile.open(filename.c_str(), ios::binary); 
    inFile.read((char*)this, sizeof(*this)); 
} 

聲明:

class User 
{ 
private: 
    string username; 
    string realname; 
    string password; 
    string hint; 
    double gpa; 
    vector<Course> courses; 
public: 
    double PredictGPA(); 
    void ChangePassword(); 
    void SaveToFile(); 
    void LoadFromFile(string filename); 

    void SetUsername(string _username){username = _username;} 
    string GetUsername(){return username;} 
    void SetRealname(string _realname){realname = _realname;} 
    string GetRealname(){return realname;} 
    void SetPass(string _password){password = _password;} 
    string GetPass(){return password;} 
    void SetHint(string _hint){hint = _hint;} 
    string GetHint(){return hint;} 
}; 
+1

是'User'一個POD類型?它是否具有與gcc和vc相同的內存佈局? –

+1

請給出類聲明 –

+1

可能不是海灣合作委員會的「怪異」,但你做出的一些假設並不成立。看着廣泛的鑄造濫用,這似乎是可能的!無論如何,只要注意「二進制序列化」是100%不可移植的,你應該看看基於文本的序列化,除非你有一個非常好的理由不要。 –

回答

2

您的class User不是POD類型,它不是Plain Old Data類型(因爲C結構是)。你不能只是讀和寫它的內存,並期望它的工作。 stringvector都不是POD,它們保持指向其動態分配數據的指針。在讀取這些內容時,嘗試訪問無效內存將導致段錯誤。更重要的是,stringvector的內容實際上根本沒有被保存,因爲它們不在對象的內存佈局中(它有時可能與string一起在SBO中工作,但它的正確但機會仍然未定義)它)。

+0

「A.更重要的是,A!」 :P –

+0

由於Small String Optimization的原因,它可能在Visual Studio上有效。 –

+0

@Minging Duck:是的,這就是'SBO'在我的回答中所代表的意思。 –

2

您需要一種方法來進行序列化和反序列化類;當你像這樣閱讀時,你的課堂不會神奇地成爲一個對象。

相反,您需要提供給您在加載/保存以選擇的某種格式存儲類的類時調用的函數,例如, XML。

所以不是

outFile.write((char*)this, sizeof(*this)); 

有一些成員函數將其轉換爲與某些格式的字符串時您加載(或任何你更容易找到一些二進制格式),可以輕鬆地分析,然後將其保存。

outFile.write(this->myserialize(), mysize); 
+0

另外mysize通常會更改,應該從myserialize()函數生成。對於許多格式來說,從myserialize()返回的char數組的頭部中加載也是一個好主意。 –

1

你不能這樣寫入string。一方面它通常動態地存儲它的數據,即不在對象內部,另一方面你不能依賴它的任何特定佈局。

向量也有類似的問題,你似乎沒有考慮過排序和填充。

簡而言之,你做出的假設並不成立。

通常,不要在字節級別上混淆複雜(非POD)對象。使用某些文本格式序列化,使用對象的公共成員函數來提取和恢復其狀態。

你認爲JSON?

1

像字符串等東西可能包含指針 - 在這種情況下,你的方法可能會出現可怕的錯誤。

您需要serialise的數據 - 即,將其轉換爲一系列字節。

然後,在讀取數據時,您只需讀取字節,然後從中創建對象。新的指針將是正確的。

0

如果你留在這條路線,我會寫字符串的長度而不是null終止它。在加載時更容易分配。有很多要考慮二進制格式。每個字段都應該有一些類型的ID,以便在錯誤的地方或程序的不同版本中找到它們。在你的文件的開頭還要寫出你使用的endianess和你的整數的大小等。或者決定一切的標準大小和endianess。我總是用這樣的代碼編寫網絡和文件存儲。有更好的現代方法。還要考慮使用緩衝區並創建Serialize()函數。

良好的現代替代品包括:sqlite3的,XML,JSON

未經測試的例子:

class object 
{ 

Load() 
{ 
    ifstream inFile; 
    int size; 

    inFile.open("filename", ios::binary); 

    inFile.read(&size, 4); 
    stringA.resize(size); 
    inFile.read(&stringA[0], size); 

    inFile.read(&size, 4); 
    stringB.resize(size); 
    inFile.read(&stringB[0], size); 

    inFile.close();   //don't forget to close your files 
} 
Save() 
{ 
    ofstream outFile; 
    int size; 

    outFile.open("filename", ios::binary); 

    size = stringA.size(); 
    outFile.write(&size, 4); 
    outFile.write(&stringA[0], size); 

    size = stringB.size();  
    outFile.write(&size, 4); 
    outFile.write(&stringA[0], size); 
    outFile.close(); 
} 

private: 
std::string stringA 
std::string stringB 
}; 
+0

事情是,這是一個Intro C++類。我已經多了一些;這些孩子在我嘗試實現一個時不知道矢量是什麼。 JSON或XML可能有點多。 – Dahud

+0

@Dahud:聽起來好像對你來說也有點過分,如果你使用的矢量如此不正確:) –