2010-10-30 69 views
0

我有以下User.h,它包含幾個屬性(字符串)。 User.cpp具有所有的定義。當從隨機訪問文件中讀取對象時,C++程序崩潰

//User.h 
#ifndef USER_H 
#define USER_H 
#include<iostream> 
#include <cstring> 

using namespace std; 

class User{ 

    string username; 

    public: 
     User(); 
     string getUsername() const;      
     void setUsername(string); 

}; 
#endif 

我使用另一個類, 「文件」,從.dat文件隨機訪問的

//File.h 
#ifndef FILE_H 
#define FILE_H 
#include "User.h" 

class File{ 
    public: 

      void loadUser(); 
      bool addUser(User&); 

}; 
#endif 

文件類定義

//File.cpp 
#include<cstring> 
#include<iostream> 
#include<iomanip> 
#include<fstream> 

#include "User.h" 
#include "File.h" 

using namespace std; 

User tempUser; 
fstream usersFile; 

void File::loadUser(){ 
    usersFile.open("users.dat", ios::in | ios::binary); 

    usersFile.seekg(0); 

    // commenting the following lines prevented the issue 
    usersFile.read(reinterpret_cast<char *>(&tempUser), sizeof(tempUser)); 

    cout<<tempUser.getUsername().c_str(); 

    usersFile.close(); 
} 

bool File::addUser(User& user){ 

    usersFile.open("users.dat", ios::out | ios::ate | ios::binary); 

    // no issue when writing to file 
    usersFile.write(reinterpret_cast<const char *>(&user), sizeof(user)); 

    usersFile.close(); 

    cout<<"User added"; 
} 

我插入新用戶/用戶視圖運行時遇到上述問題。雖然沒有編譯問題。

處理內部具有「字符串屬性」的對象時,是否有任何問題?

請幫

回答

1

我認爲問題在於你正在將C++代碼與C心態混合在一起。 你應該在這裏做的是使用提取操作符opeartor>>()以及C++ IO流。這與使用C標準IO以及read()函數相反。要寫入對象,請使用插入運算符operator<<()而不是C write()函數。

使用字符串使用std::string。本課程提供operator<<()operator>>()。因此,您可以說std::string s,然後io << sio >> s其中io是一些C++ IO流對象。這將做正確的事(tm)。這裏的哲學是std::string類知道比你,一個用戶更好,如何序列化一個std::string對象。所以讓它做到這一點,與< <和>>運營商。

繼續這個想法,作爲User的作者,你比任何人都更瞭解如何序列化一個User對象。因此,請爲您的課程的用戶提供< <和>>運營商作爲服務。當你完全忘記了如何正確序列化一個User對象時,「你的班級的用戶」很可能是你從現在開始的一週。 (或者,你認爲你還記得,但實際上你忘記了一個細節,導致代碼中出現錯誤)。例如:

// in User.h 
#include <string> 
#include <iosfwd> // forward declarations of standard IO streams 

namespace mine { 
class User { 
    User(const std::string& name) : username(name) { } 
    friend std::ostream& operator<<(std::ostream&, const User&); 
    friend std::istream& operator>>(std::istream&, User&); 

private: 
    std::string username; 
}; 

std::ostream& operator<<(std::ostream& out, const User& u) 
{ 
    return out << u.username; 
} 

std::istream& operator>>(std::istream& in, User& u) 
{ 
    return in >> u.username; 
} 
} // namespace mine 

從這裏開始,以節省用戶的文件,你說

std::ofstream f("filename"); 
User u("John"); 
f << u; 

就是這樣。讀取用戶:

std::ifstream f2("filename"); 
f2 >> u; 

將代碼封裝到名稱空間中是一種很好的做法。通過顯示自動完成功能可以看到多少個符號,IDE可以很好地顯示這個問題。你會看到全球範圍內有多少混亂。通過將代碼包裝在名稱空間中,可以將其分組在一個作用域名稱下,從而在全局名稱範圍中節省更多的麻煩。這只是關於整潔。如果你把你的代碼放在你自己的名字空間中,那麼你可以選擇你想要的函數,類或變量的名字,只要你以前沒有選擇它。如果你不把它放在命名空間中,那麼你需要與其他人分享名字。這就像是一個宣稱自己的領土的臭鼬,只有沒有臭味。

在那個筆記上,我建議你從你的頭部脫掉using namespace std。這將std名稱空間中的所有符號都納入了標題中所有文件的範圍。這是一個不好的做法。如果你願意,只能在實現文件中說using namespace std,但不能在頭文件中說。當然,有些人會說這是一個壞主意。我個人認爲,如果你意識到在特定的實現文件中可能存在名稱衝突的事實,那很好。但至少你知道using聲明的位置:它在你的實現文件中,並且只會導致該實現文件中的衝突。這是一種槍,(一個塑料水槍,但仍然是一把槍),只有你可以射擊(溼)你自己的腳,沒有其他人。在我看來,這是完全正確的。

0

是的,std :: string類是不普通的舊數據,換句話說,它包含指針。 如果以這種方式保存/加載字符串類,則指向的數據將不會被加載/保存,只有指針的值。

此外sizeof(tempUser)將不包括字符串指向的文本的大小。

你的解決方案是改變你如何讀/寫數據。 一種方法是使用boost :: serialization來處理像std :: string這樣的數據類型。 另一種方法是將每個字符串寫入文本文檔中的單獨一行(不是二進制模式),然後使用readline將它們讀回。

0

字符串是一個對象,這意味着您沒有寫入它的內容。

嘗試編寫一個用戶並檢查該文件以查看我的意思。你正在閱讀的是一些指針,指向無效的內存位置。

0

而不是構建自己的自定義序列化代碼,像Google Protocol Buffers可能會做你想要的東西,用較少的努力。它非常適合將簡單的結構化數據從一個地方傳遞到另一個地方。

1

您不能讀取那樣的非POD類型。一個字符串不是POD類型。 What are POD types in C++?

有幾種方法可以正確讀取字符串,具體取決於它們的存儲方式。

對於文本文件:

如果字符串就是一個字,用空格兩側分開,你可以使用普通的老式>>操作符。如果它不止一個單詞,則可以將其存儲在自己的行中,並使用getline。

對於二進制文件:

存放在空字符串終止形式。一次讀取一個字符,檢查空字符。或者,用一個存儲它的大小的整數前置該字符串。當你讀它時,首先讀入整數,然後讀入那麼多字符。

相關問題