Home > OS >  When will the destructor be called?
When will the destructor be called?

Time:12-17

#include <memory> 
#include <iostream>

class Token { public:
  Token() { std::cout << "Token()"; }
  ~Token() { std::cout << "~Token()"; }
};

template <class T> 
std::unique_ptr<T> foo(T t0) {
    return std::unique_ptr<T>(new T(t0)); };

int main() {
    Token&& t = Token();
    auto ptr = foo<Token>(t); 
    return 0;
}

On which occasions will the destructor be called?

I think it will be called firstly when we call Token(), it creates a temporary object which is destructed immediately, then in the foo() function when t0 is destructed, and then when main() ends, and ptr goes out of scope.

But my friend says otherwise, so how will it actually be?

CodePudding user response:

When a scope ends and the automatic objects of that scope are destroyed, their destructors will be called in the reverse order the objects were created:

int main() {
    Token&& t = Token(); // Token1 constructed
                         // lifetime of Token1 extended because it was bound to t
    auto ptr = foo<Token>(t); // creates a copy of Token1 for the argument of foo: Token2
                              // Token3 constructed by foo in dynamic memory
                              // and bound to ptr, which
                              // resides in automatic memory
    // Token2 (temporary copy) is automatically destroyed
    return 0;
    // Last automatic object is destroyed: ptr
    //   thus, uniqe_ptr destroys Token3
    // t is destroyed. This destroys Token1 because
    //                 its lifetime-extending reference
    //                 went out of scope
}

Demo

If you modify your Token class slightly you can observe this live:

class Token {
  inline static int C = 1;
  int c = C  ;
public:
  Token(Token const&) : Token() {}
  Token(Token&&) : Token() {}
  Token() { std::cout << "Token(" << c << ")\n"; }
  ~Token() { std::cout << "~Token(" << c << ")\n"; }
};

Output:

Token(1)
Token(2)
Token(3)
~Token(2)
~Token(3)
~Token(1)

(live demo)

CodePudding user response:

@bitmask's explanation about life-time is pretty clear. Besides, if you want to get the best performance, maybe you could implement the move constructor for your class Token, and then:

template <class T>
std::unique_ptr<T> foo(T& t0) {                 // pass-by-reference
    return std::make_unique<T>(std::move(t0));
};

As you have used rvalue reference Token&& t to extend the temporary Token object's lifetime in function int main() and the rvalue reference t is a lvalue, so auto foo(T &) will deduce T as class Token and accept t as its parameter t0 according to Overload resolution. Furthermore, std::move cast lvalue t0 to rvalue reference and std::make_unique call the constructor which satisfies std::is_nothrow_constructable_v<Token,Token&&> is true, the move constructor and copy constructor are both candicate functions. If you have implemented Token(Token&&), move constructor will be called. Otherwise is copy constructor.

  • Related