1

我想問你如何爲以下類寫一個拷貝構造函數(和運算符=)。複製構造函數和動態分配

類節點存儲每個節點的座標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; 

}

我不想創建每個節點的淺表副本,而不是每個節點的深層副本。我可以問你一個帶拷貝構造函數的示例代碼嗎?

每個節點都可以多次指向。讓我們有這樣的情況,當3個節點N [1],N [2],N [3]被存儲在NodesList NL1:

N [1]點至n [2]

N [ 2]指向N [3]

N [3]指向N [2]

A]我們的拷貝構造過程中的節點n [1]。它創建一個新對象n [1] _new,由舊對象n [1] _old的副本表示。從n [1] _old指向的節點n [2]仍然不存在,因此n [2] _new也必須被創建...設置從n1_new到n2_new的指針。

B]然後處理第二點n [2]。它不能創建兩次,n [2] _new在A中創建]。但是,指向節點n [3]不存在,所以新對象n [3] _new被創建爲舊對象n [3] _old的副本。從n2_new到n3_new的指針被設置。

C]節點n [3] _new已經被創建並且n [2] _new。從n3_new到n2_new的指針設置並沒有其他對象將被創建...

所以拷貝構造函數應該檢查對象是否在過去已經建立或有不...

一些引用計數可能會有所幫助......

+0

'setNode'的定義不合法。只有構造函數可以有初始化器。此外,行不應該都具有相同的縮進級別;縮進應該反映塊的嵌套級別。 – outis 2010-03-06 20:50:59

+0

你也可以考慮重命名'Node :: setNode'和'Node :: n'來描述關係,比如'Node :: setParent'和'Node :: parent'或'Node :: setReferent'和'Node :: referent'。就目前而言,節點和節點的節點之間沒有區別。 – outis 2010-03-06 23:58:33

回答

0

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(初始地)是引用其節點的唯一位置。如果一個臨時的NodeList超出範圍,一個糟糕的實現將會泄漏包含的列表Node

一個解決方案是宣佈NodeList自己的Node s。只要你不添加Node到多個NodeList(通過NodeList::push_backNodeList::operator[] & C),NodeList的方法,可以在必要(例如在NodeList::~NodeListNodeList::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*NodeList應該存儲shared pointersNode::n應該是weak pointer以防止所有權週期。

+0

您的代碼非常有用,謝謝... – justik 2010-03-07 10:10:26

0

不應從標準庫容器繼承(因爲它們缺少虛擬析構函數)。相反,將它們作爲成員變量包含在您的類中。

既然你想有一個深拷貝,你需要這些:(三規則)

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

一個更好的想法可能是避免使用指針完全,只是把你的節點在合適的容器。

+0

但有一點問題。每個節點可以多次指向。使用上面提到的複製構造函數會導致重複的對象。 n1-> setNode(n2); n3-> setNode(n2); 節點n2將被創建兩次... – justik 2010-03-06 12:03:52

+0

我不認爲節點構造函數不能做任何事情。我想,既然容器有關於鏈的結構的所有可用數據,它可以分析節點並建立一個類似的結構。 – UncleBens 2010-03-06 12:28:52

+0

恐怕,上面寫的複製構造函數會創建重複的對象......它不檢查指出的對象n是否尚未創建或已經存在......在任何情況下都創建了指出的對象。 – justik 2010-03-06 20:17:16

0

我只是使用std :: list <節點>而不是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); 
     push_back(n); 
    } 
}
0

顯然,每個節點只允許指向同一列表中的另一個節點?否則,列表的「深層複製」需要更多的定義。它不應該連接到原始的NodeList?它不應該連接到任何原始節點?是否將正在複製的列表中的節點副本添加到某個其他列表或自由浮動?

如果所有Node-to-Node指針都被限制在NodeList中,那麼也許你應該存儲索引而不是指針,那麼不需要特殊的處理。

1

有我的問題的解決方案。的溶液中加入一個新的數據成員n_ref存儲節點n的新的優化版本:用於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()); 
       } 
      } 
     } 
    } 

Node (const Node *node) 
{ 
    x = node->x; 
    y = node->y; 
    n = node->n; 
    n_ref = node->n_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;} 

複製構造創建節點的淺表副本