Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

push_back using the move constructor does not call the destructor?

Tags:

c++

The following code creates a temporary object A and pushes it into a vector. The copy constructor is deleted and the move constructor is called during the push_back. I am not sure the design of this code is correct, and surely there is a memory leak.

#include<iostream>
#include<vector>
using namespace std;

class A
{
private:
  char* ptr; 

public:
  A(const string& str)
  {
    ptr = new char[str.size()];
    copy(str.begin(), str.end(), ptr);
    cout << ptr << " Constructor\n" ;
  }

  A(const A& a) = delete;    // copy constructor
  
  A( A&& a)                
  {
    cout << "Move constructor\n";
    ptr = a.ptr;
    a.ptr = nullptr;
  }

  ~A()
  {
    cout << ptr << " Destructor\n";
    delete[] ptr;
    ptr = nullptr;
  }

  void print()
  {
    cout << ptr << endl;
  }
};



int main()
{
  vector<A> v;
  v.reserve(5);

  v.push_back( A("hello")  );
  v[0].print();
  cout << "here" << endl;
  
  return 0;
}

The output is:

    hello Constructor
    Move constructor

Why the print function doesn't print, and the destructor is not called? Thanks

like image 453
Cantaro Avatar asked Feb 18 '21 10:02

Cantaro


People also ask

Does Push_back copy or move?

Since C++11, push_back will perform a move instead of a copy if the argument is an rvalue reference.

Does Push_back make a deep copy?

It does not make deep copies, since cv::Mat 's are shared pointers. You have to use clone() or similar when adding to the vector images .


2 Answers

As pointed out in a comment, your problem lies here:

~A()
{
  cout << ptr << " Destructor\n";
  delete[] ptr;
  ptr = nullptr;
}

By the time you reach the destructor the object has already been moved from and ptr == nullptr. The char* overload of << expects a null-terminated string. If ptr does not point to a null-terminated string you invoke undefined behavior.

You get the expected output if you change it to

cout << static_cast<void*>(ptr) << " Destructor\n";

Because the overload for void* just prints the value of the pointer.

Working Demo

like image 198
463035818_is_not_a_number Avatar answered Oct 07 '22 03:10

463035818_is_not_a_number


Your code has undefined behavior on account of you essentially doing

std::cout << (char*)nullptr;

Recall that your moved-from object has its pointer set to nullptr.

That's a precondition violation of those standard library inserters:

[ostream.inserters.character]

template<class charT, class traits>
  basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& out, const charT* s);
template<class charT, class traits>
  basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& out, const char* s);
template<class traits>
  basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>& out, const char* s);
template<class traits>
  basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>& out, const signed char* s);
template<class traits>
  basic_ostream<char, traits>& operator<<(basic_ostream<char, traits>& out,
                                          const unsigned char* s);

3 Preconditions: s is not a null pointer.

Add a null check, or just don't print the pointer value in the destructor.

like image 20
StoryTeller - Unslander Monica Avatar answered Oct 07 '22 01:10

StoryTeller - Unslander Monica