0

I don't know how come the following example output, can anyone tell me? Thanks in advance!

#include <iostream>
#include <algorithm>

class A
{
public:

    // Simple constructor that initializes the resource.
    explicit A(size_t length)
        : mLength(length), mData(new int[length])
    {
        std::cout << "A(size_t). length = "
    << mLength << "." << std::endl;
    }

    // Destructor.
    ~A()
    {
  std::cout << "~A(). length = " << mLength << ".";

  if (mData != NULL) {
            std::cout << " Deleting resource.";
      delete[] mData;  // Delete the resource.
  }

  std::cout << std::endl;
    }

    // Copy constructor.
    A(const A& other)
      : mLength(other.mLength), mData(new int[other.mLength])
    {
  std::cout << "A(const A&). length = "
    << other.mLength << ". Copying resource." << std::endl;

  std::copy(other.mData, other.mData + mLength, mData);
    }

    // Copy assignment operator.
    A& operator=(const A& other)
    {
  std::cout << "operator=(const A&). length = "
           << other.mLength << ". Copying resource." << std::endl;

  if (this != &other) {
      delete[] mData;  // Free the existing resource.
      mLength = other.mLength;
            mData = new int[mLength];
            std::copy(other.mData, other.mData + mLength, mData);
  }
  return *this;
    }

    // Move constructor.
    A(A&& other) : mData(NULL), mLength(0)
    {
        std::cout << "A(A&&). length = " 
             << other.mLength << ". Moving resource.\n";

        // Copy the data pointer and its length from the 
        // source object.
        mData = other.mData;
        mLength = other.mLength;

        // Release the data pointer from the source object so that
        // the destructor does not free the memory multiple times.
        other.mData = NULL;
        other.mLength = 0;
    }

    // Move assignment operator.
    A& operator=(A&& other)
    {
        std::cout << "operator=(A&&). length = " 
             << other.mLength << "." << std::endl;

        if (this != &other) {
          // Free the existing resource.
          delete[] mData;

          // Copy the data pointer and its length from the 
          // source object.
          mData = other.mData;
          mLength = other.mLength;

          // Release the data pointer from the source object so that
          // the destructor does not free the memory multiple times.
          other.mData = NULL;
          other.mLength = 0;
       }
       return *this;
    }

    // Retrieves the length of the data resource.
    size_t Length() const
    {
        return mLength;
    }

private:
    size_t mLength; // The length of the resource.
    int* mData;     // The resource.
};

#include <vector>

int main()
{
   // Create a vector object and add a few elements to it.
   std::vector<A> v;
   v.push_back(A(25));
   v.push_back(A(75));

   ::std::cout << "----------------------" << std::endl;

   // Insert a new element into the second position of the vector.
   //v.insert(v.begin() + 1, A(50));
   return 0;
}

ouput:

A(size_t). length = 25.
A(A&&). length = 25. Moving resource.
~A(). length = 0.
A(size_t). length = 75.
A(A&&). length = 75. Moving resource.
A(const A&). length = 25. Copying resource.   // how come these two lines?
~A(). length = 25. Deleting resource.
~A(). length = 0.
----------------------
~A(). length = 25. Deleting resource.
~A(). length = 75. Deleting resource.
Dan
  • 3,221
  • 7
  • 27
  • 24
  • 1
    You do know that `std::vector` is allocating memory dynamically, and that when it extends its memory it needs to copy all the data it already have? – Some programmer dude Jan 28 '19 at 13:54
  • Help us by telling us which part you don't understand and/or what did you expect. –  Jan 28 '19 at 13:55
  • 1
    To go along with what @Someprogrammerdude said, in order to get `vector` to use move operations instead of copy operations when the vector reallocates, you must mark your move constructor/assignment operator as `noexcept`. See [How to enforce move semantics when a vector grows?](https://stackoverflow.com/questions/8001823/how-to-enforce-move-semantics-when-a-vector-grows) – 0x5453 Jan 28 '19 at 14:02

1 Answers1

4

std::vector has to either move or copy its internal state while resizing once its capacity is exhausted. However, it doesn't know that it's safe to move the A's it's storing (the two calls to push_back) since the move operations aren't marked noexcept. Because std::vector doesn't know it's safe, it's erring on the side of caution and copying the values.

// Move constructor.
 A(A&& other) noexcept : mData(NULL), mLength(0) { /* ... */ }

// Move assignment operator.
A& operator=(A&& other) noexcept { /* ... */ }

With those changes, I get the following output:

A(size_t). length = 25

.
A(A&&). length = 25. Moving resource.
~A(). length = 0.
A(size_t). length = 75.
A(A&&). length = 75. Moving resource.
A(A&&). length = 25. Moving resource.
~A(). length = 0.
~A(). length = 0.
----------------------
~A(). length = 25. Deleting resource.
~A(). length = 75. Deleting resource.

You could also reserve sufficient capacity (v.reserve(2); before calling push_back), but if your vector ever has to resize you'll start copying As again.

Stephen Newell
  • 7,330
  • 1
  • 24
  • 28