STL Набор указателей.Скопировать конструктор выпуск - PullRequest
3 голосов
/ 28 октября 2011

Я занимаюсь разработкой проекта на C ++ под Ubuntu 11.10 с использованием последней версии NetBeans. Я буду публиковать только минимальные части кода, относящиеся к проблеме. Допустим, у меня есть следующий код для проблемы с графиком:

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 в этот момент будет содержать указатели на старые узлы, поскольку новые еще не существуют.

Это мой первый пост здесь. Я надеюсь, что предоставил четкую и достаточную информацию. Я могу уточнить, был ли я недостаточно связан со своей проблемой. Любая помощь будет принята с благодарностью.

Ответы [ 3 ]

4 голосов
/ 28 октября 2011

Я делал то же самое раньше. Это сложно:

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 голос
/ 28 октября 2011

В вашем примере n1 и n2 будут указывать на одни и те же экземпляры узлов. Если один из них выходит из области видимости и удаляет узлы, другой указывает на освобожденную память.

Если вы используете необработанные указатели в узлах и переходах, вы также должны предоставить операторы присваивания и деструкторы в дополнение к конструкторам копирования для классов Network и Node.

Вместо этого используйте умные указатели. В вашем случае, если «копировать указатель на объект» является семантикой копирования, использование shared_ptr избавит вас от написания конструктора копирования и т. Д. Вручную - реализация по умолчанию сделает эту работу. Если семантика копирования - «сам объект копирования»:

  • Если производительность не является проблемой - сохраняйте объект в контейнерах, используйте копию по умолчанию c-tor и т. Д.

  • Если важна производительность - напишите свой собственный экземпляр c-tor и т. Д. Для обоих классов (или рассмотрите возможность использования третьей части или написания собственного copy_ptr / clone_ptr)

1 голос
/ 28 октября 2011

Учитывая, что вы используете необработанные указатели для своего ключевого элемента, вы не можете использовать конструктор копирования по умолчанию, но это довольно просто сделать самостоятельно, если ваша структура 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, это действительно выходит за рамки конструкторов копирования.Вы захотите написать отдельную функцию «копирования», которая начинается в какой-то момент и сканирует всю структуру, воспроизводя ее по пути.

...