我正在使用最新版本的NetBeans在Ubuntu 11.10下用C ++开发一个项目。我只发布与问题相关的代码的最小部分。假设我有以下代码用于图表类型的问题:
typedef map<Node*, double, DereferenceCompare> Transitions;
class Node {
int _nodeNumber;
Transitions _transitions;
}
每个Node对象都包含指向其他Node对象的指针映射。现在我们有:
typedef set<Node*, DereferenceCompare> Nodes;
class Network {
Nodes _network;
}
问题:我在为网络类编写复制构造函数时感到茫然。我想要实现的是能够做到以下几点:
Network n1;
Network n2(n1);
//Have both n1 and n2 identical in structure but distinct in memory (deep copy).
我是否正确地遵循以下假设:如果我为Node类编写复制构造函数,则还需要复制Transitions容器。此时的Transitions容器将保留指向旧节点的指针,因为新节点尚不存在。
这是我在这里的第一篇文章。我希望我提供了清晰而充分的信息。我可以进一步澄清我是否与我的问题缺乏连贯性。任何帮助将不胜感激。
答案 0 :(得分:4)
之前我完成了同样的事情。这很棘手:
Network::Network(const Network& b) {
//old to new mapping
std::unordered_map<Node*, Node*> mapper(b._network.size());
// duplicate all nodes without links
for(auto iter = b.begin(); iter != b.end(); ++iter) {
Node* new_node = new Node();
try {
_network.insert(new_node);
} catch (std::bad_alloc& e) {
delete new_node;
throw;
}
mapper[iter->first] = _network; //and map the old Nodes to new ones
new_node->_nodeNumber = iter->_node_number;
}
// THEN map the links
for(auto iter = b.begin(); iter != b.end(); ++iter) {
Node* new_node = mapper[iter->first];
//for each link in the old one
for(auto iter2 = iter->_transitions.begin();
iter2 != iter->_transitions.end();
++iter2)
{
//link to the corresponding new node
Node* connection = mapper[iter2->first];
new_node->_transitions[connection ] = iter2->second;
}
}
}
[编辑]现在异常安全
另请注意,除了编译之外,我没有尝试以任何方式验证代码。我记得这就是我多年前遇到同样问题时所做的事情。
答案 1 :(得分:1)
鉴于您正在使用原始指针作为关键元素,您不能使用默认的复制构造函数,但如果您的Node
结构是树结构(即没有循环到担心)
Network(const Network& other)
{
if (this != &other)
{
for (auto it = other._network.begin(); it != other._network.end(); ++it)
{
_network.insert(new Node(*it));
}
}
}
Node(const Node& other)
{
if (this != &other)
{
_nodeNumber = other._nodeNumber;
for (auto it = other._transitions.begin(); it != other._transitions.end(); ++it)
{
_transitions[new Node(*it->first)] = it->second;
}
}
}
更简单的解决方案是存储Node
元素本身,以便容器可以自动管理内存,或使用表示所需语义的智能指针。
如果允许Node
结构中的循环,那实际上超出了复制构造函数的范围。您将需要编写一个单独的“复制”功能,该功能从某个点开始并扫描整个结构,并在此过程中再现它。
答案 2 :(得分:1)
在您的示例中,n1和n2将指向节点的相同实例。如果其中一个超出范围并删除节点,则另一个将指向释放的内存。
如果在节点和转换中使用原始指针,除了网络和节点类的复制构造函数之外,还必须提供赋值运算符和析构函数。
改用智能指针。 在您的情况下,如果“将指针复制到对象”是一个复制语义,使用shared_ptr将使您免于手动编写复制构造函数等 - 默认实现将完成这项工作。 如果复制语义是“复制对象本身”:
如果性能不是问题 - 将对象存储在容器中,请使用默认复制c-tor等
如果性能很重要 - 为两个类编写自己的副本c-tor等(或考虑使用第三部分或编写自己的copy_ptr / clone_ptr)