1

I came across this link and I saw this program:

#include <iostream>
 
template<class T>
class Auto_ptr4
{
    T* m_ptr;
public:
    Auto_ptr4(T* ptr = nullptr)
        :m_ptr(ptr)
    {
    }
 
    ~Auto_ptr4()
    {
        delete m_ptr;
    }
 
    // Copy constructor
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4(const Auto_ptr4& a)
    {
        m_ptr = new T;
        *m_ptr = *a.m_ptr;
    }
 
    // Move constructor
    // Transfer ownership of a.m_ptr to m_ptr
    Auto_ptr4(Auto_ptr4&& a)
        : m_ptr(a.m_ptr)
    {
        a.m_ptr = nullptr; // we'll talk more about this line below
    }
 
    // Copy assignment
    // Do deep copy of a.m_ptr to m_ptr
    Auto_ptr4& operator=(const Auto_ptr4& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;
 
        // Release any resource we're holding
        delete m_ptr;
 
        // Copy the resource
        m_ptr = new T;
        *m_ptr = *a.m_ptr;
 
        return *this;
    }
 
    // Move assignment
    // Transfer ownership of a.m_ptr to m_ptr
    Auto_ptr4& operator=(Auto_ptr4&& a)
    {
        // Self-assignment detection
        if (&a == this)
            return *this;
 
        // Release any resource we're holding
        delete m_ptr;
 
        // Transfer ownership of a.m_ptr to m_ptr
        m_ptr = a.m_ptr;
        a.m_ptr = nullptr; // we'll talk more about this line below
 
        return *this;
    }
 
    T& operator*() const { return *m_ptr; }
    T* operator->() const { return m_ptr; }
    bool isNull() const { return m_ptr == nullptr; }
};
 
class Resource
{
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource destroyed\n"; }
};
 
Auto_ptr4<Resource> generateResource()
{
    Auto_ptr4<Resource> res(new Resource);
    return res; // this return value will invoke the move constructor
}
 
int main()
{
    Auto_ptr4<Resource> mainres;
    mainres = generateResource(); // this assignment will invoke the move assignment
 
    return 0;
}

Now I see that this program is fine, except for this part:

// Copy assignment
// Do deep copy of a.m_ptr to m_ptr
Auto_ptr4& operator=(const Auto_ptr4& a)
{
   // Self-assignment detection
   if (&a == this)
      return *this;
 
   // Release any resource we're holding
   delete m_ptr;
 
   // Copy the resource
   m_ptr = new T;
   *m_ptr = *a.m_ptr;
 
    return *this;
}

I don't understand why we have to do delete m_ptr here. Besides, what happens if m_ptr wasn't dynamically allocated yet? Then we will delete an invalid pointer. Can't we just leave the pointer as it is without deleting it, like this?

// Copy assignment
// Do deep copy of a.m_ptr to m_ptr
Auto_ptr4& operator=(const Auto_ptr4& a)
{
   // Self-assignment detection
   if (&a == this)
      return *this;
 
   *m_ptr = *a.m_ptr;

    return *this;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
cuong.pq
  • 137
  • 1
  • 1
  • 4
  • Avoid owning bare pointers in actual programs. I mean, it's an example of how to implement a (bad) smart pointer, but there is no need to do that in real programs because the standard library provides you with those. – eerorika Jul 29 '20 at 02:24

1 Answers1

1

I don't get why do we have to do delete m_ptr in here.

It's necessary, otherwise you might have memory leak because the memory newed before won't be deleteed.

what happens if m_ptr wasn't dynamically allocated yet, then we will delete an invalid pointer.

If Auto_ptr4 manages nothing, m_ptr would alwasy be nullptr. The default constructor initializes m_ptr as nullptr, and after move operation m_ptr of moved-object is assigned as nullptr too. Performing delete on null pointer is fine, which does nothing.

If expression evaluates to a null pointer value, no destructors are called, and the deallocation function may or may not be called (it's unspecified), but the default deallocation functions are guaranteed to do nothing when passed a null pointer.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405