Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why two object constructed by destructors are called for three times

Here is my implementation of some C++11 study examples. I let all the constructors and destructor to print to console. But surprisingly, I get constructor called twice but destructor three times.

The one seems unexpected is at 0x7fff5fbff6d0. When this object is created? But why no constructor call is associated?

Why this is happening?

template<typename T>
class ArrayWrapper{
public:
    ArrayWrapper():data_(nullptr), size_(0){
        cout << "Default ctor called "<< this <<endl;
    }

    ArrayWrapper(size_t n, const T& val) : data_(new T[n]), size_(n){
        cout << "ctor_n_val called "<< this << endl;
        for_each(data_, data_+size_, [&](T& elem){ elem=val; });
    }

    ArrayWrapper(const ArrayWrapper& other): data_(new T[other.size_]), size_(other.size_)
    {
        cout << "copy ctor called "<< this <<endl;
        copy(other.data_, other.data_+other.size_, data_);
    }

    ArrayWrapper(ArrayWrapper&& other): data_(other.data_), size_(other.size_)
    {
        cout << "move ctor called"<<endl;
        other.data_ = nullptr;
        other.size_ = 0;
    }

    ArrayWrapper<T>& operator=(const ArrayWrapper& other){
        cout << "copy assignment called" <<endl;
        if(this != &other){
            delete data_;
            data_ = new T[other.size_];
            copy(other.begin(), other.end(), begin());
            size_ = other.size_;
        }
        return *this;
    }

    ArrayWrapper<T> operator=(ArrayWrapper&& other){
        cout << "move assignment called " <<this << " <- " <<&other <<endl;
        swap(size_, other.size_);
        swap(data_, other.data_);
    }

    ~ArrayWrapper(){
        cout <<"Destroying " << this << " Size " << size_ <<endl;
    }
    typedef T* iterator;
    typedef const T* const_iterator;

    T* begin() {
        return data_;
    }

    T* end(){
        return data_ + size_;
    }

    const T* begin() const {
        return data_;
    }

    const T* end() const {
        return data_ + size_;
    }

    const T* cbegin() const {
        return data_;
    }

    const T* cend() const {
        return data_ + size_;
    }

    size_t size(){
        return size_;
    }
public:
    T* data_;
    size_t size_;
};

template<typename T>
ArrayWrapper<T> make_array(size_t n, const T& val){
    cout <<"Factory method called"<<endl;
    return ArrayWrapper<T>(n, val);
}

template<typename T>
std::ostream& operator<<(std::ostream& os, const ArrayWrapper<T>& arr){
    for(const T& elem: arr){ os << elem << ", ";}
    return os;
}

int main(){
    size_t n = 10;
    ArrayWrapper<int> a4(n, 1);
    a4 = make_array(n, 4); // move assignment:
    cout << "A4: " << a4 << endl;
}

Output:

$ g++-mp-4.8 -std=c++11 move.cpp 

$ ./a.out 

ctor_n_val called 0x7fff5fbff6b0

Factory method called

ctor_n_val called 0x7fff5fbff6e0

move assignment called 0x7fff5fbff6b0 <- 0x7fff5fbff6e0

Destroying 0x7fff5fbff6d0 Size 0

Destroying 0x7fff5fbff6e0 Size 10

A4: 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 

Destroying 0x7fff5fbff6b0 Size 10
like image 453
xiaochuanQ Avatar asked Dec 15 '22 11:12

xiaochuanQ


1 Answers

Your move assignment operator should return a reference:

ArrayWrapper<T>& operator=(ArrayWrapper&& other)
//             ^

Since you have it returning by value, but there is no return statement, you’re invoking undefined behaviour. You should implement it like the copy assignment operator, except of course moving resources instead of copying them:

ArrayWrapper<T>& operator=(ArrayWrapper&& other){
    if(this != &other){
        delete[] data_;
        size_ = other.size_;
        data_ = other.data_;
        other.size_ = 0;
        other.data_ = nullptr;
    }
    return *this;
}

Also, note the use of delete[] to delete dynamically allocated arrays.

like image 187
Jon Purdy Avatar answered Jan 18 '23 23:01

Jon Purdy