【问题标题】:How do I save a templated array of objects to file?如何将模板化的对象数组保存到文件中?
【发布时间】:2017-05-29 22:53:32
【问题描述】:

我有一门课叫

Array<T>

您可以创建任何类型的数组。

template <typename T>
    class Array
    {
    private:
        T *m_array;
        int m_size;
        ...

例如,

Array <int> myArray(32) 

是一个大小为 32 的 int 类型的数组。它可以存储基本类型或复杂对象。例如,

Array<Monster> monsters(32)

可以容纳一组怪物对象。无论使用哪种类型,我都想从磁盘保存和加载阵列。

其中一个对象,比如 Actor,有一个 std::string 类型的成员变量(名称)。因此,它存储为

Array<Actor> actors(32)

我今天意识到 C 的 I/O 函数对 std::string 一无所知,因此从文件加载 std::string 会导致关闭时崩溃。我想将我的保存/加载功能升级到 C++ 的等效功能。我的假设是这将解决我保存和加载具有 std::string 类型成员变量的对象的问题。

我原来的保存/加载功能: (由于模板的工作方式,它们位于标题中,我应该提到它们更正式地是 Array 或 Array::save() 和 Array::load() 的成员。)

            bool save(const string filename)
        { 
            FILE *fOut = NULL; 
            int written = 0;

            // Validate the array
            if (!isValidArray())
                return false;

            // Open the file
            fOut = fopen(filename.c_str(), "wb");
            if (fOut == NULL)
                return false; 

            // Write the array's size to file.
            fwrite(&m_size, sizeof(int), 1, fOut);

            // Write the array to file.
            written = fwrite(m_array, sizeof(T), m_size, fOut);
            fclose(fOut);

            // Validate if the array was written correctly
            if (written != m_size)
                return false; 

            return true;
        }

加载:

bool load(const string filename)
        {
            FILE *fIn = NULL;
            int read = 0;
            int size = 0;  

            // Open the file
            fopen_s(&fIn, filename.c_str(), "rb");
            if (fIn == NULL)
                return false;

            // Read the array's size from file.
            fread(&size, sizeof(int), 1, fIn);

            // Rleease the old array 
            release();

            // Initialize the new array
            if (!init(size))
                return false;

            // Read the array from file.
            read = fread(m_array, sizeof(T), size, fIn);
            fclose(fIn);

            // Validate if the array was written correctly.
            // If not, clean up the array object.
            if (read != size)
            {
                if (m_array != NULL)
                {
                    delete[] m_array;
                    m_array = NULL;
                    m_size = 0;
                }

                return false;
            } 

            return true;
        }

总的来说,我想将这些转换为 C++ 的文件处理。

这是我对 save() 的 C++ 尝试:

        bool save(const string filename)
        {  
            ofstream fOut; 

            // Validate the array
            if (!isValidArray())
                return false;

            // Open the file
            fOut.open(filename.c_str(), std::ios::binary | std::ios::out);
            if (!fOut.is_open())
                return false; 

            // Write the array's size to file.
            fOut << m_size;  

            // Write the array to file. ???? 
            fOut.write(m_array, m_size);

            fOut.close();

            return true;
        }

所以,我的问题是当它的模板类型可能是基本数据类型、结构或类时,如何将数组保存到文件中。我的第一个假设是:

// Write the array to file. ???? 
fOut.write(m_array, m_size);

任何想法都会有所帮助。谢谢。

发现我需要序列化,我为我的 Actor 类重载了 operator

std::ofstream & X2D::operator<<(std::ofstream & output, const Actor & p)
{ 
    // insert magical code here 
    return output;  
}

【问题讨论】:

  • 你要找的东西叫做序列化。
  • “由于模板的工作方式,它们位于标题中” 顺便说一句,您应该考虑将这些定义移出类定义,以使代码更清晰和更好。它们仍然可以在标题中。
  • “我有一个名为 [..] 的类,你可以创建任何类型的数组。” std::array 有什么问题?
  • 这个问题相当于“如何在 C++ 中序列化任意类型的数据?”尽管这是一个非常广泛的问题(您是否研究过任何选择?),但基本上可以回答“您没有”。
  • 唯一可以任意序列化的类型是 POD 类型。您可以使用模板来处理这种情况。对于非 POD 类型,我假设它们为序列化提供了适当的 operator&lt;&lt;。由于您的容器提供了保存操作,因此您可以将容器记录为要求其元素定义正确的operator&lt;&lt; 以进行序列化。

标签: c++ arrays io


【解决方案1】:

考虑让您的save 函数成为 Array 的成员并根据需要重载它。您可能会发现 C++11 中的 std::enable_if 功能非常有用,它只允许对您的代码准备好的类型进行序列化。你的

 fOut.write(m_array, m_size);

不会从头开始工作,您需要自己实现它。在撰写本文时,C++ 在这方面还不是 Java。

【讨论】:

  • save() 和 load() 是 Array 的成员函数。我在上面添加了一条注释。
  • 就像我写的那样,与在 C 中一样,管理数据的序列化主要是您(或仅仅是您的类)关心的问题。如果您对有限数量的类型使用模板化,请手动为这些类型专门化保存/加载功能。如果您尝试编写一个序列化其成员的通用容器,那么您就有麻烦了。 traits 的概念可能很有用——您可能会以 Array&lt;T&gt; 的实例化需要 T 公开适当的序列化接口的方式来制作容器。在后一种情况下,您只需在数组的保存/加载中调用此类方法。
  • @Phil 最简单的方法是您在问题底部的 for 循环,并为您希望存储的所有自定义对象编写适当的 &gt;&gt;&lt;&lt; 运算符。文字粗暴且效率低下,但使用起来非常容易。
【解决方案2】:

这最终成为我的解决方案。重载运算符 > 帮助我的类在保存的同时保持灵活性。

我将在下面发布代码,以防它对任何人有用。字符串保存看起来有点乱,可能会改进,但它成功保存/加载长度,然后保存/加载 std::string。

std::ostream& operator<<(std::ostream& os, const Monster& obj)
{  
int length = obj.m_name.length() + 1;  
char buffer[1080];

strcpy_s(buffer, 1080, obj.m_name.c_str());

os << length;
os.write(buffer, strlen(buffer) + 1);
os << endl;

os << obj.m_x << endl;
os << obj.m_y << endl;
os << obj.m_hp << endl;

return os;
 }

 std::istream& operator>>(std::istream& is, Monster& obj)
 { 
int length;
char buffer[1080];  

is >> length;
is.readsome(buffer, length);

std::string sBuffer(buffer, length); 
obj.m_name = sBuffer;

is >> obj.m_x;
is >> obj.m_y;
is >> obj.m_hp;

return is;
 }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 2011-01-26
    • 2011-04-07
    相关资源
    最近更新 更多