我想将一个Item写入二进制文件,关闭它,然后再次打开它来读取它。代码简单明了,使用Visual Studio 2008编译和运行时没有错误。
然而,当使用GCC编译器运行时,它会出现“段错误”。
我做错了什么?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
class Item
{
private:
string itemID;
string itemName;
string itemState;
public:
Item( const string& id = "i0000", const string& name = "Zero item", const string& state = "not init" )
: itemID( id ) , itemName( name ) , itemState( state )
{
}
string& operator []( int x )
{
if ( 0 == x )
return itemID;
if ( 1 == x )
return itemName;
if ( 2 == x )
return itemState;
return ( string& )"";
}
const string& operator []( int x ) const
{
if ( 0 == x )
return itemID;
if ( 1 == x )
return itemName;
if ( 2 == x )
return itemState;
return ( string& )"";
}
public:
friend istream& operator >>( istream& i, Item& rhs )
{
cout << " * ItemID: ";
getline( i, rhs.itemID );
cout << " - Item Name: ";
getline( i, rhs.itemName );
cout << " - Item State: ";
getline( i, rhs.itemState );
return i;
}
friend ostream& operator <<( ostream& o, const Item& rhs )
{
return o << "ID = " << rhs.itemID
<< "\nName = " << rhs.itemName
<< "\nState = " << rhs.itemState << endl;
}
};
void write_to_file( const string& fn, const Item& item )
{
fstream outf( fn.c_str(), ios::binary | ios::out );
Item temp( item );
outf.write( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
outf.close();
}
void read_from_file( const string& fn, Item& item )
{
fstream inf( fn.c_str(), ios::binary | ios::in );
if( !inf )
{
cout << "What's wrong?";
}
Item temp;
inf.read( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
item = temp;
inf.close();
}
int main()
{
string fn = "a.out";
//Item it( "12", "Ipad", "Good" );
//write_to_file( fn, it );
Item temp;
read_from_file( fn, temp );
cout << temp;
return 0;
}
答案 0 :(得分:9)
这两行:
outf.write( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
和
inf.read( reinterpret_cast<char *>( &temp ), sizeof( Item ) );
错了。您正在编写对象的二进制布局,包括std::string
个实例。这意味着您正在编写指向文件的指针值并将其读回。
你不能简单地从文件中读取指针并假设它们指向有效的内存,特别是如果它由一个临时的std::string
实例保存,当它超出范围时应该释放它的析构函数中的内存。我很惊讶你能用任何编译器“正确”运行它。
您的程序应编写内容并使用operator<<
和operator>>
方法将其读回。它应如下所示:
void write_to_file( const string& fn, const Item& item )
{
fstream outf( fn.c_str(), ios::binary | ios::out );
outf << item << std::endl;
outf.close();
}
void read_from_file( const string& fn, Item& item )
{
fstream inf( fn.c_str(), ios::binary | ios::in );
if( !inf )
{
cout << "What's wrong?";
}
inf >> item;
inf.close();
}
BONUS :您的代码存在一些怪癖。
幸运的是,这个陈述目前在您的程序中未使用(该方法未被调用)。
return ( string& )"";
它无效,因为您将返回对临时字符串对象的引用。请记住,字符串文字""
不是std::string
对象,您无法获得std::string&
类型的引用。你应该提出异常,但你可以逃脱:
string& operator []( int x )
{
static string unknown;
if ( 0 == x )
return itemID;
if ( 1 == x )
return itemName;
if ( 2 == x )
return itemState;
return unkonwn;
}
这是一个糟糕的解决方案,因为该字符串是通过引用返回的。它可以被调用者修改,因此它可能并不总是返回您认为会""
的值。但是,它将删除程序的未定义行为。
对.close()
对象的std::fstream
方法调用不是必需的,当对象超出范围时,析构函数会自动调用它。插入电话会产生额外的混乱。
此外,冗余命名约定是什么?
class Item
{
private:
string itemID;
string itemName;
string itemState;
// ...
};
给他们ID
,name
和state
打电话有什么问题?