объект, содержащий умный указатель на себя, который не сбрасывается до того, как объект выходит из области видимости - PullRequest
0 голосов
/ 11 апреля 2019

Наткнулся на эту ошибку, где класс содержит указатель на себя - и он указывал на тот же экземпляр.Вот самый маленький фрагмент кода, который содержит эту проблему ...

class Node
{
public:
    Node()
    {
        std::cout << "constructor" << std::endl;
    }

    ~Node()
    {
        std::cout << "destructor" << std::endl;
    }

    void storeChild(boost::shared_ptr<Node> ptr)
    {
        m_ptr = ptr;
    }

private: 
    boost::shared_ptr<Node> m_ptr;
};

int main()
{
    boost::shared_ptr<Node> bc_ptr(new Node());
    bc_ptr->storeChild(bc_ptr); // this line is the bug 
    return 0;
}

Мои вопросы: 1. Был ли когда-либо удален объект Node?Я знаю, что умные указатели должны управлять этими вещами, поэтому нам не нужно удалять.Но похоже, что ссылка, переданная в класс с помощью storeChild, никогда не сбрасывается.Означает ли это, что у меня будет утечка памяти?2. Есть ли способ использовать умные указатели, которые предотвращают это?Очевидно, что метод storeChild должен иметь указатель на другой узел, но как вы это предотвратите?

Если я запускаю эту программу, деструктор никогда не вызывается.

1 Ответ

1 голос
/ 11 апреля 2019

Да, это утечка памяти, так как общий указатель в Node никогда не будет удален.Подумайте:

#include <iostream>
#include <memory>

class Node {
    std::shared_ptr<Node> m_ptr;

public:
    Node() { std::cout << "constructor\n"; }
    ~Node() { std::cout << "destructor\n"; }
    void storeChild(std::shared_ptr<Node> ptr) { m_ptr = ptr; }
    long use_count() { return m_ptr.use_count(); }
};

int main() {   
    Node* n = new Node;
    {
        std::shared_ptr<Node> bc_ptr(n);
        bc_ptr->storeChild(bc_ptr); // this line is the bug

        std::cout << n->use_count() << "\n";     // prints 2
    }
    std::cout << n->use_count() << "\n";         // prints 1

    // the Node pointed to by n is leaked
}

Возможно, это будет еще более очевидно, если вы сделаете так:

#include <iostream>
#include <memory>

class Node {
    std::shared_ptr<Node> m_ptr;

public:
    Node() : m_ptr(this) { std::cout << "constructor\n"; }
    ~Node() { std::cout << "destructor\n"; }
};

int main() {
    new Node;
}

Если вы попытаетесь:

int main() {
    delete new Node;
}

Деструктор Node будетбыть вызванным дважды.Во-первых, вашим delete, а затем shared_ptr, когда он удален.

Предотвращение может быть сложным, если возможно иметь круговое владение, например: A-> B-> C-> A... Вы, вероятно, должны использовать std::weak_ptr, если это риск.

...