Dan
Dan

Reputation: 3341

C++11 rvalue issue

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.

Upvotes: 0

Views: 35

Answers (1)

Stephen Newell
Stephen Newell

Reputation: 7863

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.

Upvotes: 4

Related Questions