Зачем создавать экземпляр вызывающего деструктора shared_ptr? - PullRequest
1 голос
/ 06 мая 2020

Может кто-нибудь объяснить, почему деструктор класса bar вызывается в строке, где инициализируется объект того же типа?

    #include <memory>
    #include <iostream>

    using namespace std;

    class bar
    {
      public:
        bar() {}

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

    class foo
    {
      public:
        foo(std::shared_ptr<bar> barP) {}
    };


    int main() {

        std::shared_ptr<foo> f;
        std::cout << "before init " << std::endl;
        f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
        std::cout << "after init"  << std::endl;
    }

Вывод:

before init 
destructor called 
after init

Ответы [ 3 ]

4 голосов
/ 06 мая 2020

Этот оператор:

f = std::shared_ptr<foo>(new foo(std::shared_ptr<bar>(new bar())));
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

создает временный shared_ptr, который выходит за пределы области видимости в конце оператора. В этот момент shared_ptr уходит, забирая с собой bar (поскольку в живых не осталось копий shared_ptr).

Но если вы измените foo на это:

class foo
{
  public:
    foo(std::shared_ptr<bar> barP) { m_bar = barP; }
    std::shared_ptr<bar> m_bar;
};

Тогда результат, который вы получите, вероятно, будет тем, что вы ожидали, потому что foo поддерживает копию shared_ptr до тех пор, пока он (foo) не выйдет из области видимости, и эта копия сохраняет bar живым :

before init 
after init
destructor called

Живая демонстрация

1 голос
/ 06 мая 2020

Это потому, что экземпляр bar живет только до тех пор, пока продолжительность конструктора foo. Таким образом, он создается, передается в shared_ptr, а затем в конструктор foo. Как только этот конструктор будет выполнен (даже если он находится в той же строке), само выражение будет выполнено, и, таким образом, shared_ptr будет выполнено и разрушит.

В конце main прямо перед cout, у вас все еще есть shared_ptr для foo в f, но безымянный shared_ptr для вашего bar объекта уже «вышел из области видимости».

0 голосов
/ 06 мая 2020

Прежде всего, вы не должны создавать экземпляр shared_ptr таким образом, используйте make_shared для создания shared_ptr. Пожалуйста, проверьте измененный код -

#include <memory>
#include <iostream>

using namespace std;

class bar
{
  public:
    bar() {}

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

class foo
{
  public:
    foo(std::shared_ptr<bar> barP) {}
};


int main() {

    std::shared_ptr<foo> f;
    std::cout << "before init " << std::endl;
    std::shared_ptr<bar> b = std::make_shared<bar>();
    f = std::make_shared<foo>(b);
    std::cout << "after init"  << std::endl;
}

Результат вышеуказанной программы -

before init 
after init
destructor called 

Обычно то, что происходит с вашим кодом, вы передаете new bar конструктору foo. И никто не владеет общим указателем bar. Итак, счетчик ссылок становится 0 и удаляется, поэтому вызывается деструктор.

Дальнейшее чтение : make_shared vs new

...