1

Я хотел бы спросить вас, как написать конструктор копирования (и operator =) для следующих классов.Конструктор копирования и динамическое распределение

Узел класса хранит координаты 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 (унаследованных от станд :: вектор) хранит все динамически выделенные узлы

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 узла п [1], п [2], п [3] сохраняются в NodesList NL1:

п [1] указывает на п [2]

п [ 2] указывает на п [3]

п [3] указывает на п [2]

А] Наш процесс конструктор копирования узел п [1]. Он создает новый объект n [1] _new, представленный копией старого объекта n [1] _old. Узел n [2] указал на n [1] _old еще не существует, поэтому 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

+0

Вы также можете рассмотреть возможность переименования 'Node :: setNode' и' Node :: n' более описательного отношения, например, к 'Node :: setParent' и' Node :: parent' или 'Node :: setReferent' и 'Node :: referent'. Как бы там ни было, нет различия между узлом и узлом узла. – outis

ответ

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. До тех пор, пока вы не добавить Node более чем одной NodeList (через NodeList::push_back, NodeList::operator[] & с), методы NodeList «s могут удалять узлы, когда это необходимо (например, в 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*. NodeList следует хранить shared pointers. Node::n должен быть weak pointer, чтобы предотвратить циклы владения.

+0

Ваш код очень полезен, спасибо ... – justik

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

+0

Я не думаю, что конструктор Node ничего не может сделать. Я полагаю, что, поскольку в контейнере имеются все данные о структуре цепей, он может анализировать узлы и настраивать аналогичную структуру. – UncleBens

+0

Я боюсь, что созданный выше конструктор копирует создает повторяющиеся объекты ... Он не проверяет, не был ли уже создан или уже создан заостренный объект n ... Указанный объект создается в любом случае. – justik

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 хранения новый Verion из узла п:

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