2

I am implementing a IntArray Class for learning C++. I must admit I haven't fully understood r and lvalues and move constructors, yet. I wanted to try it out to see if my code is working, but I do not know why {IntArray array = IntArray(5);} doesn't call my implemented move constructor. I thought this would be a case for it.

#include "IntArray.h"
IntArray::IntArray()
    :data(nullptr), count(0), capacity(0) {std::cout << "Default Constructor called" << std::endl;}

IntArray::IntArray(int size)
    :data(new int[size]), count(size), capacity(size)  {std::cout << "Constructor called with size: " << size << std::endl;}

IntArray::~IntArray() {
    std::cout << "Destructor called" << std::endl;
    delete[] data; //rest is stack allocated and gets freed with end of scope
}

//COPY CONSTRUCT & ASSIGN OP
IntArray::IntArray(const IntArray& rhs) 
    :data(new int[rhs.count]), count(rhs.count), capacity(rhs.count) //warum nicht mit capacity? wir wollen doch eine exakte kopie?
{
    std::cout << "Copy Constructor called" << std::endl;
    std::copy(rhs.data, rhs.data + rhs.count, data); //pointer arithmetik?
}
IntArray& IntArray::operator=(const IntArray& rhs) {
    if (&rhs == this) //check for selfassign
        return *this;

    //if capacity of lhs is NOT big enough, reallocate new
    if (capacity < rhs.capacity) { 
        delete[] data;
        data = new int[rhs.count];
        capacity = rhs.count;
    }   
    count = rhs.count;
    std::copy(rhs.data, rhs.data + rhs.count, data);
    return *this;
}

//MOVE CONSTRUCT & ASSIGN OP
IntArray::IntArray(IntArray&& rhs)
    :data(rhs.data), count(rhs.count), capacity(rhs.capacity)
{
    std::cout << "Move Constructor called" << std::endl;

    rhs.data = nullptr;
    rhs.count = 0;
    rhs.capacity = 0;
}
IntArray& IntArray::operator=(IntArray&& rhs) {
    if (&rhs == this) //self assignment?
        return *this;
    std::cout << "Move assignment operator called" << std::endl;

    //steal
    delete[] data;
    data = rhs.data;
    count = rhs.count;
    capacity = rhs.capacity;

    //Reset old obj to prevent double freeing
    rhs.data = nullptr;
    rhs.count = 0;
    rhs.capacity = 0;

    return *this;
}
Jan
  • 88
  • 5
  • 2
    [Copy elision](https://en.cppreference.com/w/cpp/language/copy_elision) – Igor Tandetnik Dec 30 '18 at 15:37
  • 2
    Off-topic, but your assignment operator has a flaw. You deallocate memory before you call `new[]`. If `new[]` throws an exception, you have a corrupted object. Instead of duplicating the code in the copy constructor, call it by creating a temporary and swapping things out -- `{IntArray t(rhs); std::swap(t.data, data); std::swap(t.capacity, capacity), std::swap(t.count,count); return *this;}` -- In other words, use the [copy-swap idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) – PaulMcKenzie Dec 30 '18 at 16:00

1 Answers1

2

That you don't see move construction or move assignment is simply a result of optimization! Please take a look for "copy elision" https://en.cppreference.com/w/cpp/language/copy_elision

If you are using gcc you can tell the compiler to don't optimize it by:

g++ -O0 main.cpp -fno-elide-constructors

Now the result is:

Constructor called with size: 5
Move Constructor called
Destructor called
Destructor called
Klaus
  • 24,205
  • 7
  • 58
  • 113
  • Looking for this apparently starnge behavior, I came across this question and [another](https://stackoverflow.com/q/47960154/5825294). Can you please elaborate a bit to answer a question I've copied from a comment (since I totally share the exact same doubt)? The question is: _[...] this happens whenever a temporary object is created for the sole purpose of being copied and subsequently destroyed (copy-ellision). **But this sounds exactly like the intended use case for move constructor?**_. – Enlico Apr 22 '19 at 18:07
  • @EnricoMariaDeAngelis: Can you please ask a new question. Your topic is far away from this one. Thanks – Klaus Apr 23 '19 at 06:36