C ++多态加载/保存

时间:2012-09-17 12:24:33

标签: c++ polymorphism

我正在保存并重新加载从公共基础派生的一堆不同对象到文件,显然我需要存储类名(或类似的东西),以便在重新加载时创建正确的对象类型。 / p>

保存很简单:

class Base 
{
  virtual string className() const = 0;

  void saveToFile()
  {
    write(className());
    ... other writing stuff
  }
}

class Derived1: public Base 
{
  string className() const { return "Derived1"; };
  ...
}

class Derived2: public Base 
{
  string className() const { return "Derived2"; };
  ...
}

并且,如果你不介意重复字符串,加载很容易......

static Base * Base::factory(const String &cname)
{
  if (cname == "Derived1")
    return new Derived1; 
  else if (cname == "Derived2")
    return = new Derived2; 
  else ...
}

void load()
{
  String cname = readString();

  Base * obj(Base::factory(cname);

  obj->readIt();
}

但是,重复的字符串会冒犯我的DRY感觉:理想情况下,className()可能是static virtual,但这是不允许的。我有一种感觉,我错过了一个明显的“清洁”方式,但我还没有看到它。有什么建议吗?

注意:好的,使用工厂方法略微调整代码。请注意,这实际上并没有解决问题!

注意#2:上面的代码并不是最终工厂模式的完美表现。我不关心基类和派生类之间的不必要的链接,或者扩展层次结构的潜在困难。我正在寻找能够简化代码而不是使代码复杂化的答案。

6 个答案:

答案 0 :(得分:3)

关于你能做的最好的事情,你可以通过将if包裹在工厂类中来清理它。

答案 1 :(得分:3)

这里有两件事。首先,要避免两次写出名称, 我过去曾经使用过以下内容:

class Derived : public Base
{
    //  ...
    static char const* className() { return "Derived"; }
    virtual char const* getClassName() const { return className(); }
                 //  overrides virtual function in the base class...
};

此外,如果您希望能够从外部读取该类 来源,你需要某种静态工厂功能,其中 使用这些功能的地图注册自己。我会继续这样做 在Base

class Base
{
    // ...
protected:
    class Factory
    {
    protected:
        Factory( std::string const& type );
    public:
        virtual Base* constructFromFile( std::istream const& source ) const = 0;
    };
    typedef std::map <std::string, Factory const*> FactoryMap;
    static FactoryMap& factories();

    template <typename Derived>
    class ConcreteFactory : public Factory
    {
    public:
        ConcreteFactory() : Factory( Derived::className() ) {}
        virtual Base* constructFromFile( std::istream const& source ) const
        {
            return new Derived( source );
        }
    };

public:
    static Base* readFromFile( std::istream& source );
};


Base::FactoryMap&
Base::factories()
{
    static FactoryMap theOneAndOnly;
    return theOneAndOnly;
}

Base::Factory::Factory( std::string const& type )
{
    std::pair <FactoryMap::iterator, bool> results 
        = factories().insert( std::make_pair( type, this ) );
    assert (results.second);
}

Base* Base::readFromFile( std::istream& source )
{
    std::string type = readType( source );
    FactoryMap::const_iterator factory = factories().find( type );
    if ( factory == factories().end() ) {
        throw UnknownType(...);
    }
    return factory->second->constructFromFile( std::istream& source );
}

最后,对于每个派生类,您必须定义构造函数 取std::istream&和静态实例 Base::ConcreteFactory <Derived>。 (如上所述,这必须是一个 静态成员。)

答案 2 :(得分:2)

您显然需要 Factory 模式。 在这里阅读更多 http://www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus

答案 3 :(得分:2)

您可以使用工厂方法(AKA虚拟构造函数)这在“设计模式”一书中以及整个互联网的许多地方进行了解释 - 您可以使用这些术语进行Google搜索。 StackOverflow中可能已经讨论过这个问题了。

答案 4 :(得分:2)

Id'将字符串映射到函数,创建类:

std::hash_map<std::string, std::function<Base*()> creators;

然后,您可以创建填充地图的功能

template <typename T> void add()
{
    creators.insert(std::pair(T::class_name(), []()-> Base* { return new T(); }));
}

用法很简单:

//factory constructor
add<Derived1>();
add<Derived2>();

//creation
Base* r = 0;
auto it = creators.find(string);
if (it != creators.end()) {
    r = (*it)();
}

答案 5 :(得分:0)

我遇到了同样的问题,在本文中找到了一个非常简洁的答案: Industrial Strenght Pluggable Factories 足够接近詹姆斯坎泽的答案,但我建议你阅读本文并亲自尝试。