C++ избегать приведения в производном коде класса для двойного контейнера / дизайна содержащихся объектов

У меня есть двойные базовые классы для класса контейнера и содержащегося объекта (в моем случае для универсального класса графа, состоящего из связанных узлов, но я предполагаю, что вопрос будет стоять во всех случаях, когда вы хотите извлечь как из класса контейнера, так и из класс объекта из двойного контейнера / содержит базовые классы). Я хочу использовать производные классы как контейнера, так и содержимого объекта. Как избежать необходимости постоянно понижать содержание объекта до производного типа объекта при доступе к контейнеру класса производного контейнера?

// a class that represents the graph
template <typename WrappedObject> class BaseGraph  {
  ... // constructor, virtual destructor, ...
public:
  std::map<WrappedObject*, BaseNode<WrappedObject> *> obj_to_node_map_;
  bool addEdge(WrappedObject *scr, WrappedObject *trg) {
    BaseNode<WrappedObject > * srcNode = getOrCreateNode(src);
    BaseNode<WrappedObject > * dstNode = getOrCreateNode(trg);
    return addEdge(srcNode, dstNode);
  }

  bool addEdge(BaseNode<WrappedObject> * src, BaseNode<WrappedObject> * trg) {
    if (src == trg) {
      return false;
    }

    src->suc_.insert(trg);
    trg->pre_.insert(src);
    return true;
  }

  BaseNode<WrappedObject>* getOrCreateNode(WrappedObject*obj){
    BaseNode<WrappedObject> *node = getNode(obj);
    if (node == nullptr) {
      node = createNode(obj);
      obj_to_node_map_[obj] = node;
    }
    return node;
  }

  BaseNode<WrappedObject>* createNode(WrappedObject* obj){
    BaseNode<WrappedObject> *node = allocateNode(obj);
    nodes_.push_back(node);
    return node;
  }
  BaseNode<WrappedObject>* getNode(WrappedObject*obj) const {
    auto it = obj_to_node_map_.find(obj);
    if (it == obj_to_node_map_.end()) {
      return nullptr;
    }
    return it->second;
  }
private:
  virtual BaseNode<Object> * allocateNode(Object *obj) = 0;

};
// a class that represents the vertices of the graph
template <typename WrappedObject>
class BaseNode {
  friend class BaseGraph<WrappedObject>;
public:
  typedef std::set<BaseNode *> Set;
  BaseNode(WrappedObject*obj):obj_(obj) {
  }
  virtual ~BaseNode() {};

  WrappedObject *obj_;
  Set suc_;
  Set pre_;
};

// derived classes for node and graph
class RealObject;
class MyNode : public BaseNode<RealObject> {
private:
  std::string name_;
  MyNode(RealObject *obj, std::string & name): BaseGraph<RealObject>(obj), name_(name) {}
...
};
class MyGraph: public BaseGraph<RealObject> {
  // constructor, virtual destructor, ...
private:
  virtual BaseNode<RealObject> * allocateNode(RealObject *obj) {
    return new MyNode(obj, this);
  }
  std::map<std::string, MyNode *> name_lu_map_;
public:
  MyNode * getNode(std:string & name) {
    return(name_lu_map_[name]);
  }
  // Have to have this otherwise any call to getNode on a MyGraph instance
  // will cause compiler error as getNode is overloaded in derived class
  MyNode * getNode(RealObject *obj) const {
    return static_cast<MyNode *>(BaseGraph<RealObject>::getNode(obj));
  }

  void myDerivedClassFunction(RealObject *obj) {
    // Lines like the following when I call a method of the base class
    // returning a base class pointer will require casting, which I find
    // ugly
    MyNode *node = static_cast<MyNode *>(getOrCreateNode(obj));
    node->name_ = <something>;
    ...
  }
};

Существует ли шаблон проектирования, который позволил бы избежать неловкого перехода к производному классу всякий раз, когда я использую методы базового класса контейнера, которые возвращают базовый класс, содержащий указатели на объекты? Я думал об использовании аргумента шаблона, чтобы определить, каким будет производный тип объекта, но я не вижу, как я могу описать в C++ тот факт, что аргумент шаблона typename происходит от другого класса. Возможно, я смотрю на это с неправильной точки зрения. Есть идеи?

0 ответов

Другие вопросы по тегам