Home > OS >  Trying to use copy and swap idiom on operator=
Trying to use copy and swap idiom on operator=

Time:10-07

While trying to implement MyVector I end up with:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class MyVector
{
    int m_size = 0;
    int m_capacity = 0;

    T* m_data = nullptr;

public:
    MyVector()
    {
        cout << "defautl ctor" << endl;

        realloc(2);
    }

    MyVector(const MyVector& v)
    : m_size(v.m_size), 
      m_capacity(v.m_capacity)
    {
        cout << "copy ctor" << endl;

        m_data = new T[m_size];
        *m_data = *v.m_data;
    }

    MyVector(MyVector&& v)
    : m_size(v.m_size), 
      m_capacity(v.m_capacity)
    {
        cout << "move ctor" << endl;

        m_data = move(v.m_data);

        v.m_data = nullptr;
        v.m_size = 0;
        v.m_capacity = 0;
    }

    MyVector& operator= (const MyVector& v)
    {
        cout << "copy assignment operator" << endl;

        // m_size = v.m_size;
        // m_capacity = v.m_capacity;
        // *m_data = *v.m_data;

        MyVector<int> copy = v;
        swap(*this, copy);

        return *this;
    }

    void push_back(const T& value)
    {

        if (!(m_size < m_capacity))
        {
            // cout << value << " size is " << m_size << " capacity is " << m_capacity << endl;

            realloc(m_size*2);
            // return;
        }

        m_data[m_size  ] = value;
    }

    T& operator[] (int index)
    {
        cout << "index " << index << " of size " << m_size << endl;

        if (!(index < m_size))
            cout << "index out of bounds" << endl;

        return m_data[index];
    }

    int size()
    {
        return m_size;
    }

    T* begin()
    {
        return &m_data[0];
    }

    T* end()
    {
        return &m_data[size()];
    }

private:
    void realloc(int new_capacity)
    {
        // cout << __func__ << " new capacity " << new_capacity << endl;

        T* data = new T[new_capacity];

        for (int i = 0; i < m_size; i  )
            data[i] = m_data[i];

        delete[] m_data;
        m_data = data;

        m_capacity = new_capacity;
    }
};


int main(int argc, char** argv)
{
    cout << "starting..." << endl;

    MyVector<int> a;
    a.push_back(7);

    MyVector<int> d;
    d = a; 

    cout << a[0] << endl;
    cout << d[0] << endl;

    return 0;
}

Where the operator= is

    MyVector& operator= (const MyVector& v)
    {
        cout << "copy assignment operator" << endl;

        // m_size = v.m_size;
        // m_capacity = v.m_capacity;
        // *m_data = *v.m_data;

        MyVector<int> copy = v; // copy and swap
        swap(*this, copy);

        return *this;
    }

However this led to what seems to be a recursive behavior. So, is my understanding of the copy and swap approach wrong? Or is there something else I'm missing?

CodePudding user response:

As stated in comments, you did not implement a swap() function for MyVector, so the statement swap(*this, copy); is calling std::swap() (one of the many pitfalls of using using namespace std;), which will invoke your operator= again, hence the recursive behavior you are seeing.

Also, your copy constructor is not implemented correctly. It is not copying all of the elements from the input array into the new array. It is copying only the 1st element.

Also, you are missing a destructor to free the allocated array.

Also, since MyVector has both copy and move constructors, your two assignment operator='s can (and should) be merged into one operator that takes a MyVector by value. This lets the compiler decide whether to call the operator with copy or move semantics depending on whether the caller is passing in an lvalue or an rvalue, respectively. The operator can then just swap whatever it is given, as the input will have already been copied or moved before the operator is entered.

Try something more like this:

#include <iostream>
#include <string>
#include <algorithm>
#include <stdexcept>

template <typename T>
class MyVector
{
    int m_size = 0;
    int m_capacity = 0;
    T* m_data = nullptr;

public:
    MyVector()
    {
        std::cout << "default ctor" << std::endl;

        realloc(2);
    }

    MyVector(const MyVector& v)
    {
        std::cout << "copy ctor" << std::endl;

        realloc(v.m_capacity);
        std::copy_n(v.m_data, v.m_size, m_data);
        m_size = v.m_size;
    }

    MyVector(MyVector&& v)
    {
        std::cout << "move ctor" << std::endl;

        v.swap(*this);
    }

    ~MyVector()
    {
        std::cout << "dtor" << std::endl;

        delete[] m_data;
    }

    MyVector& operator= (MyVector v)
    {
        std::cout << "assignment operator" << std::endl;

        v.swap(*this);

        return *this;
    }

    void push_back(const T& value)
    {
        if (m_size >= m_capacity)
        {
            // std::cout << value << " size is " << m_size << " capacity is " << m_capacity << std::endl;

            realloc(m_size * 2);
        }

        m_data[m_size] = value;
          m_size;
    }

    T& operator[] (int index)
    {
        std::cout << "index " << index << " of size " << m_size << std::endl;

        if (index < 0 || index >= m_size)
            throw std::out_of_range("index out of bounds");

        return m_data[index];
    }

    int size() const
    {
        return m_size;
    }

    T* begin()
    {
        return &m_data[0];
    }

    T* end()
    {
        return &m_data[m_size];
    }

    void swap(MyVector &other)
    {
        std::swap(m_data, other.m_data);
        std::swap(m_size, other.m_size);
        std::swap(m_capacity, other.m_capacity);
    }

private:
    void realloc(int new_capacity)
    {
        // std::cout << __func__ << " new capacity " << new_capacity << std::endl;

        T* data = new T[new_capacity];

        std::copy_n(m_data, m_size, data);
        std::swap(m_data, data);
        m_capacity = new_capacity;

        delete[] data;
    }
};

// for std::swap() to use via ADL...
void swap(MyVector &v1, MyVector &v2)
{
    v1.swap(v2);
}
  • Related