代码之家  ›  专栏  ›  技术社区  ›  justik

复制构造函数和动态分配

  •  1
  • justik  · 技术社区  · 16 年前

    我想问您如何为以下类编写复制构造函数(和运算符=)。

    类节点存储每个节点的坐标x、y和指向另一个节点的指针。

    class Node
    {
    private:
    double x, y;
    Node *n;
    
    public:
    Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {}
    void setNode (Node *nn) : n(nn) {} 
    ...
    };
    

    类NodesList(从std::vector继承)存储所有动态分配的节点

    class NodesList : public std::vector<Node *>
    {}
    

    int main()
    {
    Node *n1 = new Node(5,10,NULL);
    Node *n2 = new Node(10,10,NULL);
    Node *n3 = new Node(20,10,NULL);
    n1->setNode(n2);
    n2->setNode(n3);
    n3->setNode(n2);
    NodesList nl1;
    nl1.push_back(n1);
    nl1.push_back(n2);
    nl1.push_back(n3);
    //Copy contructor is used, how to write
    NodesList nl2(nl1);
    //OPerator = is used, how to write?
    NodesList nl3 = nl1;
    

    }

    我不想创建每个节点的浅副本,而是创建每个节点的深副本。我可以问你一个复制构造函数的示例代码吗?

    每个节点可以指向多次。假设这样的情况,当节点列表nl1中存储了3个节点n[1],n[2],n[3]时:

    n[1]指向n[2]

    n[2]指向n[3]

    n[3]指向n[2]

    A] 我们的复制构造函数处理节点n[1]。它创建一个新对象n[1]\u new,由旧对象n[1]\u old的副本表示。从n[1]\u old指向的节点n[2]仍然不存在,因此还必须创建n[2]\u new。。。设置从n1\u new到n2\u new的指针。

    B] 然后处理第二点n[2]。不能创建两次,n[2]\u new was created in A]。但是点节点n[3]不存在,因此创建了新对象n[3]\u new作为旧对象n[3]\u old的副本。设置了从n2\u new到n3\u new的指针。

    C] 节点n[3]\u new已创建,n[2]\u new已创建。从n3\u new到n2\u new的指针已设置,不会创建其他对象。。。

    一些参考计数可能会有帮助。。。

    5 回复  |  直到 16 年前
        1
  •  0
  •   outis    16 年前

    在中执行浅拷贝 NodeList::NodeList(const NodeList&)

    class NodeList {
    private:
        typedef std::vector<Node*> Delegate;
        Delegate nodes;
    
    public:
        NodeList(int capacity=16) : nodes() { nodes.reserve(capacity); }
    
        NodeList(const NodeList& from);
        virtual ~NodeList();
    
        NodeList& operator=(const NodeList& from);
    
        /* delegated stuff */
        typedef Delegate::size_type size_type;
        typedef Delegate::reference reference;
        typedef Delegate::const_reference const_reference;
        typedef Delegate::iterator iterator;
        typedef Delegate::const_iterator const_iterator;
    
        size_type size() const { return nodes.size(); }
    
        iterator begin() { return nodes.begin(); }
        const_iterator begin() const { return nodes.begin(); }
        iterator end() { return nodes.end(); }
        const_iterator end() const { return nodes.end(); }
        // ...
    };
    
    NodeList::NodeList(const NodeList& from)
        : nodes(from.size()), flags(NodeList::owner)
    {
        std::map<Node*, Node*> replacement;
        Delegate::const_iterator pfrom;
        Delegate::iterator pto;
        // shallow copy nodes
        for (pfrom=from.begin(), pto=nodes.begin(); 
             pfrom != from.end(); 
             ++pfrom, ++pto) 
        {
            replacement[*pfrom] = *pto = new Node(**pfrom);
        }
        // then fix nodes' nodes
        for (pto = nodes.begin(); pto != nodes.end(); ++pto) {
            (*pto)->setNode(replacement[(*pto)->getNode()]);
        }
    }
    

    NodeList::operator=(const NodeList&) 可以使用复制交换习惯用法,与Tronic的相同 Node::operator=(const Node&) .

    这种设计有一个潜在的内存泄漏,因为一个复制的 NodeList 是(最初)唯一引用其节点的位置。如果是临时的 超出范围,执行不好会泄漏 Node

    一个解决办法是宣布 自己的 节点 S只要你不加 不止一个 (通过 NodeList::push_back NodeList::operator[] &c) 哦, 节点列表 NodeList::~NodeList NodeList::pop_back ).

    NodeList::~NodeList() {
        Delegate::iterator pnode;
        for (pnode = nodes.begin(); pnode != nodes.end(); ++pnode) {
            delete *pnode;
        }
    }
    
    void NodeList::pop_back() {
        delete nodes.back();
        nodes.pop_back();
    }
    

    smart pointers 而不是 Node* 节点列表 应该存储 shared pointers Node::n weak pointer

        2
  •  1
  •   justik    16 年前

    我有办法解决这个问题。添加了存储节点n的新版本的新数据成员n\u ref:

    class Node
    {
    private:
    double x, y;
    Node *n, *n_ref;
    
    public:
    Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {n_ref = NULL;}
    Node * getNode() {return n;}
    Node * getRefNode () {return n_ref;}
    void setNode (Node *nn) {this->n = nn;} 
    void setRefNode (Node *nn) {this->n_ref = nn;}
    

    复制构造函数创建节点的浅层副本:

    Node (const Node *node) 
    {
        x = node->x;
        y = node->y;
        n = node->n;
        n_ref = node->n_ref;
    }
    

    NodesList的复制构造函数

        NodesList::NodesList(const NodesList& source)
        {
            const_iterator e = source.end();
            for (const_iterator i = source.begin(); i != e; ++i) {
    
                //Node* n = new Node(**i);
    
                //Node n still has not been added to the list
                if ((*i)->getRefNode() == NULL)
                {
                    //Create node
                    Node *node = new Node(*i);
    
                    //Add node to the list
                    push_back(node);
    
                    //Set this note as processed
                    (*i)->setRefNode(node);
    
                    //Pointed node still has not been added to the list
                    if ((*i)->getNode()->getRefNode() == NULL)
                    {
                        //Create new pointed node
                        Node *node_pointed = new Node ((*i)->getNode());
    
                        //Add node to the list
                        push_back(node_pointed);
    
                        //Set pointer to n
                        node->setNode(node_pointed);
    
                        //Set node as processed
                        ((*i)->getNode())->setRefNode(node_pointed);
                    }
    
                    //Pointed node has already been added to the list
                    else
                    {
                        //Set pointer to node n
                        node->setNode((*i)->getRefNode());
                    }
                }
    
                //Node n has already been added to the list
                else
                {
                    //Get node
                    Node * node = (*i)->getRefNode();
    
                    //Pointed node still has not been added
                    if ((*i)->getNode()->getRefNode() == NULL)
                    {
                        //Create new node
                        Node *node_pointed = new Node ((*i)->getNode());
    
                        //Add node to the list
                        push_back(node_pointed);
    
                        //Set pointer to n
                        node->setNode(node_pointed);
    
                        //Set node as processed
                        ((*i)->getNode())->setRefNode(node_pointed);
                    }
    
                    //Pointed node has already been added to the list
                    else
                    {
                        //Set pointer to n
                        node->setNode((*i)->getNode()->getRefNode());
                    }
                }
            }
        }
    
        3
  •  0
  •   Corwin    16 年前

    NodesList::NodesList(const NodesList& source)
    {
        const_iterator e = source.end();
        for (const_iterator i = source.begin(); i != e; ++i) {
            Node* n = new Node(**i);
            push_back(n);
        }
    }
        4
  •  0
  •   Ben Voigt    16 年前

    显然每个节点只允许指向同一列表中的另一个节点?否则,列表的“深度副本”需要更多的定义。它不应该连接到原始节点列表吗?它不应该连接到任何原始节点吗?不在被复制列表中的节点的副本是添加到其他列表中还是自由浮动?

    如果所有节点到节点的指针都被约束在NodeList中,那么也许您应该存储索引而不是指针,那么不需要特殊处理。

        5
  •  0
  •   Tronic    16 年前

    既然你想要深度复制,你就需要这些:(三法则)

    Node(Node const& orig): x(orig.x), y(orig.y), n() {
        if (orig.n) n = new Node(*orig.n);
    }
    Node& operator=(Node const& orig) {
        // The copy-swap idiom
        Node tmp = orig;
        swap(tmp); // Implementing this member function left as an exercise
        return *this;
    }
    ~Node() { delete n; }