Я хочу иметь общие переменные, которые я могу легко написать/загрузить из файла XML и, возможно, использовать в системе меню. Класс, содержащий такие переменные, будет выглядеть так:
class Entity
{
private:
VarGroup group;
Var<float> myfloat;
Var<int> myint;
public:
Entity() : group("group name"), myfloat("name", &group), myint("name2", &group) {}
};
Var
оснащен перегруженными операторами, поэтому я могу использовать их как обычные переменные. Им даются буквальные имена, а их указатели базового класса вставляются в группу. VarGroup
используется для выполнения VarGroup
действий по типам Var
, таким как импорт в/из XML.
Проблема в том, что если объекты Entity хранятся в std::vector
по значению, данные могут перемещаться в памяти, а указатели Var
в объекте VarGroup
становятся недействительными.
Возможно или даже разумно попытаться обработать этот тип копии по функциональности в Var
и VarGroup
?
Подробнее:
class VarBase
{
protected:
std::string name;
public:
VarBase(const std::string &_name) : name(_name) {}
virtual bool toXMLNode(rapidxml::xml_document<> *doc, rapidxml::xml_node<> *parent) const = 0;
virtual bool fromXMLNode(rapidxml::xml_node<> *node) = 0;
std::string getName() const;
};
class VarGroup
{
private:
std::string name;
std::vector<VarBase*> vars;
public:
VarGroup(const std::string &_name) : name(_name) {}
void insert(VarBase *var)
{
vars.push_back(var);
}
bool toXMLNode(const std::string &nodeName, rapidxml::xml_document<> *doc) const;
bool fromXMLNode(rapidxml::xml_node<> *node);
};
template<typename T>
class Var : public VarBase
{
private:
T value;
public:
Var(const std::string &_name, VarGroup *group) : VarBase(_name)
{
group->insert(this);
}
bool toXMLNode(rapidxml::xml_document<> *doc, rapidxml::xml_node<> *parent) const override;
bool fromXMLNode(rapidxml::xml_node<> *node) override;
// Implementations of operators (), =, etc.
};
Я попытался придумать подход, но это может быть опасно.
Я добавил указатель VarGroup к классу Var, чтобы сделать два связанных в обоих направлениях. Затем я добавил конструкторы копирования, которые пытаются сохранить правильную связь под копией любого класса, и это должно быть независимым от порядка.
class VarBase
{
protected:
std::string name;
public:
VarBase(const std::string &_name) : name(_name) {}
virtual bool toXMLNode(rapidxml::xml_document<> *doc, rapidxml::xml_node<> *parent) const = 0;
virtual bool fromXMLNode(rapidxml::xml_node<> *node) = 0;
std::string getName() const;
};
class VarGroup
{
private:
std::string name;
std::vector<VarBase*> vars;
public:
VarGroup(const std::string &_name) : name(_name) {}
VarGroup(const VarGroup &that)
{
name = that.name;
vars = that.vars;
for(auto base : vars)
base->pGroup = this;
}
// Swap existing VarBase pointer with new one.
void swap(VarBase *newVar, VarBase *oldVar)
{
auto it = find(vars.begin(), vars.end(), oldVar);
*it = newVar;
}
void insert(VarBase *var)
{
vars.push_back(var);
}
bool toXMLNode(const std::string &nodeName, rapidxml::xml_document<> *doc) const;
bool fromXMLNode(rapidxml::xml_node<> *node);
};
template<typename T>
class Var : public VarBase
{
friend class VarGroup;
private:
T value;
VarGroup *pGroup;
public:
Var(const std::string &_name, VarGroup *_pGroup) : VarBase(_name), pGroup(_pGroup)
{
pGroup->insert(this);
}
Var(const Var &that)
{
pGroup = that.pGroup;
value = that.value;
pGroup->swap(this, &that);
}
bool toXMLNode(rapidxml::xml_document<> *doc, rapidxml::xml_node<> *parent) const override;
bool fromXMLNode(rapidxml::xml_node<> *node) override;
// Implementations of operators (), =, etc.
};
Теперь есть два экземпляра копии:
1) Объект VarGroup
сначала копируется.
Он обращается к "скопированным" объектам Var
чтобы изменить их указатель на новую. Когда объекты Var
копируются, они копируют новый адрес VarGroup
из старого объекта Var
. Затем с помощью этого нового указателя они вызывают новый объект VarGroup
чтобы изменить свой старый адрес объекта Var
на новый.
2) Сначала объекты Var
копируются.
Конструктор копирования копирует старый групповой адрес (избыточная операция). Затем он меняет адрес Var
из "скопированной группы". Когда объект VarGroup
копируется, он уже имеет новые адреса Var
(если все объекты Var
были скопированы). Затем он обращается к новым объектам Var
чтобы изменить их указатель на новую.
Это звучит слишком сложно, но может ли это сработать?
VarGroup
действительно владел данными, например, будучи контейнером уникальных указателей?