2

I have the following class which is called CoordinatesList. It contains a dynamic array of Coordinates, where each coordinate has 3 integers; x,y, and z. In the class CoordinatesList, I have two different member operator=, I am slightly confused about what is the difference between them?

Will it work the same if I inherited the class coordinates in the class CoordinatesList

class Coordinates {//this is a complete class, do not modify it.
public:
    Coordinates() {
        x = new int; y = new int; z = new int;
        *x = *z = *y = 0;
    }
    Coordinates(int _x, int _y, int _z) {
        x = new int; y = new int; z = new int;
        *x = _x;
        *z = _y;
        *y = _z;
    }
    Coordinates(const Coordinates& rhs) { // copy constructor
        x = new int; y = new int; z = new int;
        *x = *(rhs.x);
        *y = *(rhs.y);
        *z = *(rhs.z);
    }
    ~Coordinates() {
        delete x; delete y; delete z;
    }
    void operator=(const Coordinates& rhs) {//simplified operator=
        *x = *(rhs.x);
        *y = *(rhs.y);
        *z = *(rhs.z);
    }
    int getx() const { return *x; }
    int gety() const { return *y; }
    int getz() const { return *z; }
    void setx(int _x) { *x = _x; }
    void sety(int _y) { *y = _y; }
    void setz(int _z) { *z = _z; }
    friend ostream& operator<< (ostream& out, const Coordinates& rhs) {
    out << "[" << *(rhs.x) << "," << *(rhs.y) << "," << *(rhs.z) << "]" << endl;
    return out;
    }

private:
    int *x, *y, *z;
}; //--------------------------------------------------------------



class CoordinatesList {
public:

    /*CoordinatesList &  operator=(const CoordinatesList &rhs)
    {

        if (size != rhs.size)
        {
            delete[] list;
            size = rhs.size;
            list = new Coordinates[size];
        }
        for (int i = 0; i < size; i++)
        {
            list[i].Coordinates::operator=(rhs.list[i]);
        }
        return *this;

    } */

    CoordinatesList operator=(const CoordinatesList & rhs) 
    {

        //check if sizes are differernt
        if (size != rhs.size) 
        {   
            delete[] list; //this calls ~coordinates 
            size = rhs.size; 
            list = new Coordinates[size]; 
        }
        //copy content      
        for (int i = 0; i < size; i++) {
            //list[i] = rhs.list[i]; 
//will work as operator= is defined for Coordinates
            list[i].setx(rhs.list[i].getx());
            list[i].sety(rhs.list[i].gety());
            list[i].setz(rhs.list[i].getz());
        }
        return *this; 
    }
    private:
    Coordinates * list;
    int size;
};
ZAX9732
  • 85
  • 6
  • 11
    I have a hard time imagining a good reason for using `int *x, *y, *z;` over `int x, y, z;` here. – François Andrieux May 08 '19 at 20:01
  • 3
    Why complicate things with either `list[i].Coordinates::operator=(rhs.list[i]);` or getters and setters when you can write `list[i] = rhs.list[i];`? (Which, not by coincidence, is the whole point of overloading the assignment operation.) – molbdnilo May 08 '19 at 20:10
  • `CoordinatesList` will also need a copy constructor and a destructor to satisfy the [Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three), and once you have the copy constructor written and working, you can take advantage of the [Copy and Swap Idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) to make `operator=` staggeringly simple. – user4581301 May 08 '19 at 20:18
  • 2
    Why would you inherit `Coordinates` in `CoordinatesList`? A list of `Coordinates` is a kind of `Coordinates` in the same way that an apple cart is a kind of apple. – molbdnilo May 08 '19 at 20:18
  • 2
    Piggy-backing on @molbdnilo 's comment. When you are considering inheritance, apply the [Liskov Substitution Principle](https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle). LSP is a great quick filter. If it checks out, it's worth it to investigate further. If it doesn't, don't use inheritance. – user4581301 May 08 '19 at 20:22
  • The difference between the two assignment operators is that the first one return a reference to `this` and the other one returns a copy. Other than that, the result is the same. I also agree that the whole thing can be simplified. Remove pointers, and use `std::vector` instead of CoordinateList. – Gilles-Philippe Paillé May 08 '19 at 20:25

1 Answers1

1

using CL = CoordinatesList; to save on typing.

The difference is that one returns a reference, one returns a copy. The idiomatic way is to return a reference to *this, so use this one: CL& operator=(const CL& rhs){/*...*/ return *this;} Note that having both versions defined will result in a compiler error because functions cannot differ only by their return values.

Usage of operator=:

CL a = CL(<args>);// (1)
CL b,c;
b = a; // (2)
b.operator=(a); //(3)
c = b = a; // (4)
c.operator=(b.operator=(a)); // (5)

(1) Does not call any CL::operator= but a constructor CL::CL(<args>). An object is being created, so a constructor must be called no matter the equal sign.

(2) Is only syntactic sugar for (3). Calls CL::operator= and discards any returned value.

(4) Again, its only syntactic sugar for (5). First the right operator= is evaluated and the returned value is passed to the left operator= as its argument. In this case having operator= returning a copy will indeed make a copy. That's the reason why the second option is preferred as it does not incur this additional and unneeded cost. Also this should be a good explanation for why the function returns anything at all, if it returned void then this syntax would not be possible.

Quimby
  • 17,735
  • 4
  • 35
  • 55