简体   繁体   中英

C++ - Constructor, Copy Constructor, Move Constructor, Destructor

I am trying C++ myself, and I really am getting a little confused.
I really got problems with understanding constructor / Copy Constructor/ Move Constructor / Destructor and setting them up correctly.

I want to implement a copy as well as a move constructor and a destructor into the class Container . I tried the copy constructor first and thought I got it right. Unfortunately, the destructor lets the program crash at the end for some reason when trying to do delete[] data . I think I got something wrong before that the field data is not there anymore and I copied it wrong or whatever. I hope you can help me out and understand my errors. I also tried a move constructor (underneath), which absolutely does not work.

Thanks for the help guys. Hope to be able to give something back later on :)

Container class:

#include <iostream>
#include <memory>

class Container
{
public:
Container()
{
    length = 0;
    data = nullptr;
}

Container(int lengthin):Container()
{
    length = lengthin;
    data = new double[lengthin];
    //data[lengthin];
    //double data[lengthin] = {0};
}

Container(std::initializer_list<double> listin)
        :Container((int)listin.size())
{
    std::uninitialized_copy ( listin.begin(), listin.end(), data);
}

//copy constructor - working? 
Container(const Container& other):Container(other.length)
{
    //data = other.data;
    //length = other.length;
    for (auto i=0; i<other.length; i++)
    {
        data[i] = other.data[i];
    }
}


//~Container(){length = 0;}
~Container()
{
    delete[] data;
    length = 0;
}


Container operator+(Container cin)
{
    Container cout(cin.length);
    cout.length = cin.length;
    for (auto i=0; i<cin.length; i++)
    {
        cout.data[i] = cin.data[i] + data[i];
    }
    return cout;
}

Container operator-(Container cin)
{
    Container cout(cin.length);
    cout.length = cin.length;
    for (auto i=0; i<cin.length; i++)
    {
        cout.data[i] = data[i]-cin.data[i];
    }
    return cout;
}


void print(const std::string &info) const
{
    // print the address of this instance, the attributes `length` and
    // `data` and the `info` string
    std::cout << "  " << this << " " << length << " " << data << "  "
              << info << std::endl;
}

private:
    int length;
    double *data;
};

The main programme:

int main()
{
Container a({ 1, 2, 3 });
std::cout << "  a has address " << &a << std::endl;

Container b = { 4, 5, 6 };  
std::cout << "  b has address " << &b << std::endl;

Container c(a);
std::cout << "  c has address " << &c << std::endl;

Container d = a + b;
std::cout << "  d has address " << &d << std::endl;

Container e;
std::cout << "  e has address " << &e << std::endl;

e = a + b;

//Container f(std::move(a + b));   
//std::cout << "  f has address " << &f << std::endl;

return 0;}

And the tried move constructor:

Container(const Container&& other):length(other.length), data(other.data)

Your move constructor doesn't move anything, it just copies the length and pointer (meaning it works just like the default copy-constructor). That means you will have two Container objects whose data member will point to the same memory.

When one of the object that memory is deleted, leading to undefined behavior when the second object tries to delete the same memory.

A simple way to fix this is to set the other objects length to zero and its data pointer to nullptr . Or default-initialize the current object, and then swap the two objects.

As explained by Some programmer dude, this move constructor would do :

Container(Container&& other):length(other.length), data(other.data)
{
other.length = 0;
other.data = nullptr;
}

other.data = nullptr; therefore when other's destructor is called, delete []data will have no effect (with your current code is invalidates the data array held by the Container you just moved to. other.length = 0; Because since other has no data array, it also should have no length. Note that I only posted the code because apparently you have posted a wrong answer yourself (it seems like the first answer wasn't clear enough as to what the code should be). Also because this is a constructor your do not have to worry about this->data note that with a move asignement operator you would have to first delete this->data[] in order to avoid memory leak.

The copy asignement function could be as such :

Container& operator=(const Container &other)
{
    if (this->length)
    delete[] this->data;
    this -> length = other.length;
    data = new double [this->length];
    for (auto i = 0; i<other.length; i++)
    {
        this->data[i] = other.data[i];
    }
    return *this;
}

Your copy constructor is fine (but can be simplified using a copy algorithm instead of a manual loop).

Your class is missing a copy assignment operator, a move constructor, and a move assignment operator.

And your operator+ and operator- should take their inputs by reference instead of by value, and themselves be declared as const . They are also not taking into account that the input Container may have a different length than the Container being acted on.

Try something more like this:

#include <iostream>
#include <algorithm>

class Container
{
public:
    Container() : length(0), data(nullptr)
    {
    }

    Container(int len) : Container()
    {
        length = len;
        data = new double[len];
    }

    Container(std::initializer_list<double> src) : Container((int)src.size())
    {
        std::uninitialized_copy(src.begin(), src.end(), data);
    }

    Container(const Container &src) : Container(src.length)
    {
        std::uninitialized_copy(src.data, src.data + src.length, data);
    }

    Container(Container &&src) : Container()
    {
        src.swap(*this);
    }

    ~Container()
    {
        delete[] data;
        length = 0;
    }

    void swap(Container &other) noexcept
    {
        std::swap(data, other.data);
        std::swap(length, other.length);
    }

    Container& operator=(const Container &rhs)
    {
        if (length < rhs.length)
        {
            Container tmp(rhs);
            swap(tmp);
        }
        else
        {
            length = rhs.length;
            std::uninitialized_copy(rhs.data, rhs.data + rhs.length, data);
        }
        return *this;        
    }

    Container& operator=(Container&& rhs)
    {
        rhs.swap(*this);
        return *this;        
    }

    Container operator+(const Container &rhs) const
    {
        int len = std::max(length, rhs.length);
        Container out(len);
        for (auto i = 0; i < len; ++i)
        {
            if ((i < length) && (i < rhs.length))
                out.data[i] = data[i] + rhs.data[i];
            else
                out[i] = (i < length) ? data[i] : rhs.data[i];
        }
        return out;
    }

    Container operator-(const Container &rhs) const
    {
        int len = std::max(length, rhs.length);
        Container out(len);
        for (auto i = 0; i < len; ++i)
        {
            if ((i < length) && (i < rhs.length))
                out.data[i] = data[i] - rhs.data[i];
            else
                out[i] = (i < length) ? data[i] : rhs.data[i];
        }
        return out;
    }

    void print(const std::string &info) const
    {
        // print the address of this instance, the attributes `length` and
        // `data` and the `info` string
        std::cout << " " << this << " " << length << " " << data << " " << info << std::endl;
    }

private:
    int length;
    double *data;
};

namespace std
{
    void swap(Container &lhs, Container &rhs)
    {
        lhs.swap(rhs);
    }
}

And then you can greatly simplify things by using std::vector instead of a manual array:

#include <iostream>
#include <algorithm>
#include <vector>

class Container
{
public:
    Container(size_t len = 0) : data(len)
    {
    }

    Container(std::initializer_list<double> src) : data(src)
    {
    }

    void swap(Container &other) noexcept
    {
        std::swap(data, other);
    }

    Container operator+(const Container &rhs) const
    {
        size_t thislen = data.size();
        size_t thatlen = rhs.data.size();
        size_t len = std::max(thislen, thatlen);
        Container out(len);
        for (auto i = 0; i < len; ++i)
        {
            if ((i < thislen) && (i < thatlen))
                out.data[i] = data[i] + rhs.data[i];
            else
                out[i] = (i < thislen) ? data[i] : rhs.data[i];
        }
        return out;
    }

    Container operator-(const Container &rhs) const
    {
        size_t thislen = data.size();
        size_t thatlen = rhs.data.size();
        size_t len = std::max(thislen, thatlen);
        Container out(len);
        for (auto i = 0; i < len; ++i)
        {
            if ((i < thislen) && (i < thatlen))
                out.data[i] = data[i] - rhs.data[i];
            else
                out[i] = (i < thislen) ? data[i] : rhs.data[i];
        }
        return out;
    }

    void print(const std::string &info) const
    {
        // print the address of this instance, the attributes `length` and
        // `data` and the `info` string
        std::cout << " " << this << " " << data.size() << " " << data.data() << " " << info << std::endl;
    }

private:
    std::vector<double> data;
};

namespace std
{
    void swap(Container &lhs, Container &rhs)
    {
        lhs.swap(rhs);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM