带有指针和二进制文件的类

Classes with pointers and binary files

本文关键字:二进制文件 指针      更新时间:2023-10-16

好吧,伙计们/女孩们,我已经问过这个问题,但我认为我没有解释得很好,我找不到解决方案,所以我会再次询问更多详细信息并解释更多我的问题的背景。

我有两个包含用户数据的类,我想将它们保存在二进制文件中。另一方面,我有一个模板类负责保存这些类。

必须提到一个非常重要的事实:一开始,我为我要保存的任何类选择了编码辅助类。此辅助类负责写入/读取数据。原始类具有字符串成员,辅助类具有指向 char 的指针。但是最近,为了更加简单和灵活,我选择将包含字符串类优点的原始类与具有指针的辅助类结合起来,使类在保存时更舒适。因此,我没有两个类,而有一个处理数据输入/输出和数据写入/读取的类。

此更改如下所示:

class AuxNOTE;
//Original Class: Input/Output of Data
class NOTE{
private:
    string _Category;
    string _Description;
public:
    NOTE() : _Category( "" ) , _Description( "" ) { }
    NOTE( const NOTE & note ) : _Category( note._Category ) 
                              , _Description( note._Description ) { }
    NOTE( string category , string description ) : _Category( category)                                                    
                                                 , _Description( description ) { }
    NOTE( const AuxNOTE & aux ) : _Category( aux._Category ) 
                                , _Description( aux._Description ) { }
    NOTE & operator=( const NOTE & note ){ 
         _Category = note._Category;
         _Description = note._Description;
         return *this;
}
    NOTE & operator=( const AuxNOTE & aux ){
         _Category = string( aux._Category );
         _Description = string( aux._Description );
         return *this;
}
    string GetCategory() const { return _Category; }
    string GetDescription() const { return _Description; }
    void SetCategory( string category ) { _Category = category; }
    void SetDescription( string description ) { _Description = description; }
}; 
//Auxliary Class: Writing/Reading of Data to/from binary files
class AuxNOTE{
private:
     char _Category[50];
     char _Description[255];
public:
     AuxNOTE(){ }
     AuxNOTE( const NOTE & note ){
          strcpy( _Category , note._Category );
          strcpy( _Description , note._Description);
     }
     AuxNOTE & operator=( const NOTE & note ){
          strcpy( _Category , note._Category );
          strcpy( _Description , note._Description );
          return *this;
     }
};

我现在拥有的是这样的:

//Class NOTE: Input/Output of Data and Writing/Reading to/from binary files.
// .h file
class NOTE{
private:
   char * _Category;
   char * _Description;
public:
   NOTE();
   NOTE( const NOTE & note );
   NOTE( string category , string description );
   NOTE & operator=( const NOTE & note )
   string GetCategory() const;
   string GetDescription() const;
   void SetCategory( string category );
   void SetDescription( string description );
};
// .cpp file
#include "NOTE.h"
NOTE :: NOTE() : _Category( nullptr ) ,_Description( nullptr )
{
}
NOTE :: NOTE( string description , string category )
     : _Category ( new char[ category.size() + 1 ] )
     , _Categoria( new char[ description.size() + 1 ] )
{
    strcpy( _Categoria , category.c_str() );
    strcpy( _Descripcion , description.c_str() );
}
NOTE :: NOTE (const NOTE & copy )
     : _Category( nullptr )
     , _Description nullptr )
{
    if( copy._Description != nullptr ){
        _Description =  new char[ strlen( copy._Description ) + 1 ];
        strcpy( _Description , copy._Description );
    }
    if( copy._Category != nullptr ){
        _Category = new char[ strlen( copy._Category ) + 1 ];
        strcpy( _Category , copy._Category );
    }
}
NOTE :: ~NOTE() {
    if( _Description != nullptr ) delete [] _Description;
    if( _Category != nullptr ) delete [] _Category;
}
//Get Methods
string NOTE :: GetDescription() const { return string(_Description); }
string NOTE :: GetCategory() const { return string(_Category); }
//Set Methods
void NOTE :: SetDescription( string description ){
    if( _Description != nullptr ) delete [] _Description;
    _Description = new char[ description.size() + 1 ];
    strcpy( _Description , description.c_str() );
}
void NOTE :: SetCategory( string category ){
    if( m_Category != nullptr ) delete [] _Category;
    _Category = new char[ category.size() + 1 ];
    strcpy( _Category , category.c_str() );
}
//Operators
NOTE & NOTE :: operator=( const NOTE & note ){
    if( note._Description != nullptr ) SetDescription( note.GetDescription() );
    if( note._Category != nullptr ) SetCategory( note.GetCategory() );
    return *this;
}

请注意,如果 NOTE 类适用于字符串成员,则公共接口看起来很像,但事实并非如此,因为它适用于指向字符的指针。因此,可以毫无问题地保存 NOTE 类。但是,该类根本不负责写作/阅读,但我创建了另一个类,只要这些类具有可以保存的成员,就可以保存任何类。

负责此操作的类是一个模板类,如下所示:

template< class T >
class SAVER{
private:
   vector< T > _Vector;
   string _File;
public:
   SAVER( string file );
   ~SAVER();
};

template< class T >
SAVER< T > :: SAVER( string file ) : _File( file ){
    assert( _File != "" );
    ifstream file( _File , ios::binary );
    if( file.is_open() ){
        T obj;
        while( file.read( reinterpret_cast<char*>(&obj) , sizeof(obj) ) )
            _Vector.push_back( obj );
    }
}

template< class T >
Saver< T > :: ~Saver() {
    if( _Vector.empty() )
           return;
    ofstream file( _File , ios::binary | ios::trunc );  
    assert( file.is_open() );
    auto itr = _Vector.begin();
    auto end = _Vector.end();
    while( itr != end ){
        if ( !file.write( reinterpret_cast<char*>( &itr ) , sizeof(itr) ) ) 
           break;
        itr++;
     }
}

SAVER的构造函数处理读取并将数据(例如NOTE对象)放入其向量中。销毁器处理将所有矢量的对象写入相应的二进制文件。

我必须清楚我的错误不是编译错误,而是运行时错误。

现在,这就是我遇到的问题:

当我执行整个程序时,它必须读取二进制文件,但它会中断。我用调试器打开它,我看到程序在这一行中以"分段错误错误"结束,这来自 SAVER 构造函数:

NOTE :: ~NOTE() {
    if( _Description != nullptr ) delete [] _Description; //It breaks at this line
    if( _Category != nullptr ) delete [] _Category;
}

在调试器中,我可以看到 _Description 的值,旁边出现一个内存错误,上面写着:错误:无法访问地址(值为 _Description)的内存。

为什么会这样?你看到任何错误吗?如果您需要更多信息或不了解某些内容,请告诉我。

首先,在互联网上搜索"c++ 序列化库"。 您正在执行的操作称为序列化

指针和任何包含指针的类都不能逐字写入文件。 指针是内存中的位置。 大多数操作系统不能保证程序下次执行时具有确切的内存位置。 您的程序可能会在不同的内存区域中运行,这会更改数据的存储位置。

有一些技术可以解决这个问题,例如先写入数量,然后写入数据,或者先写入数据,然后写入某种哨兵(例如 C 样式字符串中的"\0")。

请考虑不要以二进制文件的形式编写,而是使用格式化的文本表示形式。 许多平台将读入数字并将其转换为本机表示。 以二进制模式写入文件的本机表示形式,在另一个平台上许多并不相同(查找"Endianess")。 此外,大多数文本编辑器和文字处理器都可以轻松读取文本文件。 读取和解释二进制文件更加困难。

除非应用程序的瓶颈是 I/O 限制的,并且 I/O 计时至关重要,否则请考虑使用数据的文本表示形式。 它更易于阅读(尤其是在调试程序时)且易于移植。

在调试器中,我可以看到 _Description 的值,旁边出现一个内存错误,上面写着:错误:无法访问地址(值为 _Description)的内存。

当然,您无法从二进制文件中反序列化指针。您需要将其大小信息和内容存储在文件中。