使用 Metadata/Inheritance 分解多个 类 中的代码

Using Metadata/Inheritance to factor out code across multiple classes

我有两个 class 表示两个非常简单的数据库,每个都有一个 "Save" 函数,可以将 class 中的内容写入文件。由于 "Save" 函数中的代码非常相似,我想知道是否可以分解出来。

我的一位同事说这可能通过继承 and/or 元数据实现,所以我尝试通过 Google 自己研究它。但是,我找不到任何有用的东西,我仍然不确定我想做的事情是否可行。

如果可以分解,那么我想我需要让另一个 class 或函数知道每个 class 的类型并以某种方式遍历它们(元数据?)。它会检查每个数据的类型,并根据类型确定它是否正确输出到文本文件。

(我知道姓名、年龄等数据应该是私有的,但为了简单起见,我将所有内容设为 public)

class A
{
public:
  A() : name(""), age(0) {};

  void Save(void)
  {
    std::string filename = "A.txt";
    std::string data;

    data += name                + "\n";
    data += std::to_string(age) + "\n";

    std::ofstream outfile(filename);
    outfile.write(data.c_str(), data.size());
    outfile.close();
  }

  std::string name;
  int age;
};

class B
{
public:
  B() : ID(0), points(0) {};

  void Save(void)
  {
    std::string filename = "B.txt";
    std::string data;

    data += std::to_string(ID)     + "\n";
    data += std::to_string(points) + "\n";

    std::ofstream outfile(filename);
    outfile.write(data.c_str(), data.size());
    outfile.close();
  }

  int ID;
  int points;
};

int main(void)
{
  A a;
  B b;

  a.name = "Bob"; a.age    = 20;
  b.ID   = 4;     b.points = 95;

  a.Save();
  b.Save();

  return 0;
}

一个可能的解决方案是使用元编程(不确定元数据是什么意思),即重复使用公共部分的模板

template<typename T1, typename T2>
void TSave(const std::string fname, const T1& p1, const T2& p2) {
    std::string filename = fname;
    std::stringstream data;

    data << p1 << "\n";
    data << p2 << "\n";

    std::ofstream outfile(filename);
    outfile.write(data.str().c_str(), data.str().size());
    outfile.close();
}

class A {
  ...

  void Save(void) {
    TSave("A.txt", name, age);
  }

  std::string name;
  int age;
};

class B {
  ...

  void Save(void) {
    TSave("B.txt", ID, points);
  }

  int ID;
  int points;
};

Live Example

您正在寻找的是 serialization:将对象保存到文件(并且有一天,恢复对象)。

当然,您可以编写自己的序列化框架,而 Marco 的回答是朝着这个方向迈出的一个有趣的开端。但或者,您可以考虑现有的库,例如 boost::serialization :

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

class A {
private: 
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & name;
        ar & age;
    }
...
};
class B {
private: 
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & ID;
        ar & points;
    }
...
};
main() {
   A a; 
   B b; 
   ...
   {
      std::ofstream ofs("myfile");
      boost::archive::text_oarchive arch(ofs);
      arch << a << b;
   }
}  

如您所见,仍然需要说明要写入文件的内容。然而,代码被简化了:您不必担心文件管理和数据转换。它也适用于标准容器。

您不会找到自动确定 class 要保存的内容的 C++ 技巧。两个原因:

  • C++ 允许元编程,但它不是自反的:没有标准过程可以在执行时找出哪些成员组成 class。
  • 在一个对象中,一些数据可能是暂时的,即它仅在执行时表示某些内容并且取决于上下文。例如指针:您可以将指针的值保存到一个文件,但是当您稍后重新加载它时它将毫无意义(指针仅在您释放对象之前有效)。正确的方法是保存指向的对象(但在哪里,何时,如何?)。