指针的集合.复制构造函数问题

STL Set of pointers. Copy constructor issue

本文关键字:构造函数 问题 复制 集合 指针      更新时间:2023-10-16

我正在使用最新版本的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编写复制构造函数感到困惑。我想要达到的目标是能够做到以下几点:

Network n1;
Network n2(n1);
//Have both n1 and n2 identical in structure but distinct in memory (deep copy).

我在以下假设中是否正确:如果我为Node类编写复制构造函数,它还需要复制Transitions容器。此时Transitions容器将保存指向旧节点的指针,因为新节点还不存在。

这是我在这里的第一篇文章。我希望我提供了明确和充分的信息。如果我没有把我的问题讲清楚,我可以进一步澄清。

我以前做过同样的事情。这很棘手:

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;
        }
    }
}

[EDIT]现在异常安全
还要注意,除了编译之外,我没有尝试以任何方式验证代码。我只记得几年前我遇到同样的问题时我是这么做的

考虑到你正在使用原始指针的关键元素,你不能使用默认的复制构造函数,但它是相当简单的做自己,如果你的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结构中循环,那真的超出了复制构造函数的范围。您可能想要编写一个单独的"copy"函数,它从某个点开始扫描整个结构,并在整个过程中复制它。

在您的示例中,n1和n2将指向节点的相同实例。如果其中一个超出了作用域并删除了节点,那么另一个将指向释放的内存。

如果在Node和Transitions中使用原始指针,除了为Network和Node类提供复制构造函数外,还必须提供赋值操作符和析构函数。

使用智能指针代替。在您的情况下,如果"复制指向对象的指针"是一个复制语义,使用shared_ptr将使您不必手动编写复制构造函数等—默认实现将完成这项工作。如果复制语义是"复制对象本身":

  • 如果性能不是问题-在容器中存储对象,使用默认copy c- for etc

  • 如果性能很重要-为这两个类编写自己的copy c-tor等(或考虑使用第三方或编写自己的copy_ptr/clone_ptr)