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 происходит от другого класса. Возможно, я смотрю на это с неправильной точки зрения. Есть идеи?