将继承类存储到文件的更好方法

时间:2017-01-21 23:30:03

标签: c++ inheritance serialization

此代码有效,但这样做的正确方法是什么?

我的意思是,如何消除read_in函数中的switch语句,或者处理动物类或其子类中的所有读取,所以我的read_in函数可以像我的write_out函数一样简单?

我有一个vector<animal*> *animals充满了我需要在文件中写入/读取的猫和通用动物。

我省略了一些代码,所以帖子不会太大......

enum class animal_type
{
    GENERIC_ANIMAL,
    CAT
};

假设我有一个类动物

class animal
{
    animal_type m_type;
    string m_name;

    virtual void write_binary(ofstream &out)
    {
        out.write((char*)(&m_type), sizeof(m_type)); //first 'animal_type'
        out.write((char*)(&m_type), sizeof(m_type)); //second 'animal_type'
        out.write(m_name.c_str(), m_name.size()+1);
    {

    virtual void read_binary(std::ifstream &in)
    {
        in.read((char*)(&m_type), sizeof(m_type)); //read the second animal type here
        m_name = read_null_string(in);//this function returns next string from input
    }
};

和一个源自动物的类

class cat : public animal
{
    bool m_is_cute;

    void write_binary(std::ofstream &out)
    {
        animal::write_binary(out);
        out.write((char*)(&m_is_cute), sizeof(m_is_cute));
    }

    void read_binary(std::ifstream &in)
    {
        animal::read_binary(in);
        in.read((char*)(&m_is_cute), sizeof(m_is_cute));

    }
 };

我把它们写到像这样的文件

void write_out(std::ofstream &out, std::vector<animal*> *animals)
{
    int size = animals->size();
    out.write((char*)(&size), sizeof(size));

    for(animal* a : *animals)
    {
        a->write_binary(out);
    }
}

并从文件中读取它们

void read_in(std::ifstream &in, std::vector<animal*> *animals)
{
    animals->clear();
    int size;
    in.read((char*)(&size), sizeof(size));
    for(int i = 0; i< size; ++i)
    {
        animal_type type;
        //read the first 'animal_type' here
        in.read((char*)(&type), sizeof(type));
        animal *a;
        switch(type)
        {
            case(animal_type::GENERIC_ANIMAL):
            a = new animal(in);//this constructor just calls the read_binary method
            break;

            case(animal_type::CAT):
            a = new cat(in);//this constructor just calls the read_binary method
            break;
        }
        animals->push_back(a);
    }
}

1 个答案:

答案 0 :(得分:0)

以下是一个(众多)选项:

首先,我们将enum class animal_type更改为普通的枚举,因为我们希望将其用作整数。如果不允许,请将其enum class animal_type保留,然后使用static_cast<size_t>进行转换。

enum animal_type
{
    GENERIC_ANIMAL = 0, // must be 0. First index in array
    CAT,
    LAST, // must be last. Put no animals after it
    FIRST  = GENERIC_ANIMAL // makes it easy to loop FIRST to LAST
};

然后定义所有动物。

接下来,构建一个调用相应动物构造函数的函数数组。此数组必须与上面的枚举完全匹配。如果这是一个问题,请考虑改为使用std::map

std::function<animal *(std::ifstream &)> animalFactory[] =
{
    [](std::ifstream & in) {return new animal(in);},
    [](std::ifstream & in) {return new cat(in);}
};

Documentation on std::function

Documentation on Lambda expressions

接下来定义在发生坏事时要抛出的异常。可以像

一样简单
class Bogus_File_Exception: public std::exception
{
    const char* what() const
    {
        return "File read failed.";
    }
};

最后read_in成为

void read_in(std::ifstream &in, std::vector<animal*> & animals) // note the reference
{
    animals.clear();
    int size;
    if (in.read((char*)(&size), sizeof(size))) // testing for successful read
    {
        for(int i = 0; i< size; ++i)
        {
            animal_type type;
            if (in.read((char*)(&type), sizeof(type))) // testing again
            {
                if (type < animal_type::LAST)
                {
                    animals.push_back(animalFactory[type](in));
                }
                else
                {
                    throw Unknown_Animal_Exception();
                }
            }
            else
            {
                throw Bogus_File_Exception();
            }
        }
    }
    else
    {
        throw Bogus_File_Exception();
    }
}