C++ noob stuff: copies, return references, constructors.
Tags: cpp, noobstuff
Published: 2022-01-08 05:57:34

I started to learn C++ for work, and it's been "fun". One of the first things I learned is passing function arguments by reference, instead of by value (or by passing a raw pointer). You can also return references, but I haven't seen this covered explicitly, so I wrote a small program to test things out. Here is a first version.

## Gadgets

```C++
struct Gadget {
    // members
    int giovanni;
    int giorgio;
    // constructor
    Gadget(int giov, int gior): giovanni(giov), giorgio(gior) {}
    // a convenient method
    void tellMe() {
        cout << "giovanni: " << giovanni << ", " << &giovanni << "\n";
        cout << "giorgio: " << giorgio << ", " << &giorgio << "\n";
    }
};

int main() {
    Gadget g{-3,7};
    cout << "Defined a new Gadget g at " << &g << "\n";
    g.tellMe();
}
```
(I'm using `struct` here, and `class` later, for no good reason)
Running it spits out something like this.

```
Defined a new Gadget g at 0x7ffee700d918
giovanni: -3, 0x7ffee700d918
giorgio: 7, 0x7ffee700d91c
```

Interestingly, the address of `g` is the same as the one for `g.giovanni`. This is probably C++ being smart about memory. This seems to be ok. While the lines below are fine

```C++
    Gadget* p = &g;
    (*p).tellMe();
```

the compiler will complain if we try this

```C++
    Gadget* p = &(g.giovanni); // incompatible types
```

or this

```C++
    int* p = &(g.giovanni);
    (*p).tellMe(); // *p does not have a tellme method
```

## Widgets
Let's add a second class (this time a class with everything explicitly public).

```C++
class Widget {
public:
    // members
    int myInto;
    Gadget myGadget;
    // constructor
    Widget(int giov, int gior, int myo): myGadget(giov, gior), myInto(myo) {}
    // info
    void tellMe() {
        cout << "myInto: " << myInto << ", " << &myInto << "\n";
        myGadget.tellMe();}
    // getters: by value, by reference
    Gadget getGadget_val() {return myGadget;}
    Gadget& getGadget_ref() {return myGadget;}
};
```

Let's write a `main` to use this.

```C++
int main() {
    Widget w{2,-3,77};
    cout << "defined new widget w at " << &w << "\n";
    w.tellMe();

    cout << "\ncalling tellMe from w.getGadget_ref()\n";
    w.getGadget_ref().tellMe();

    cout << "\ncalling tellMe from w.getGadget_val()\n";
    w.getGadget_val().tellMe();
}
```

Running this will output something like this

```
defined new widget w at 0x7ffeec970910
myInto: 77, 0x7ffeec970910
giovanni: 2, 0x7ffeec970914
giorgio: -3, 0x7ffeec970918

calling tellMe from w.getGadget_ref()
giovanni: 2, 0x7ffeec970914
giorgio: -3, 0x7ffeec970918

calling tellMe from w.getGadget_val()
giovanni: 2, 0x7ffeec970908
giorgio: -3, 0x7ffeec97090c
```

and we see that indeed the members of `w.getGadget_val()` have different memory addresses.

## Sanity check
As confirmation, let's try modifying `getGadget_val()` and `getGadget_ref()`. Here's a complete snippet.

```C++
#include <iostream>
using std::cout;

struct Gadget {
    // members
    int giovanni;
    int giorgio;
    // constructor
    Gadget(int giov, int gior): giovanni(giov), giorgio(gior) {}
    // a convenient method
    void tellMe() {
        cout << "giovanni: " << giovanni << ", " << &giovanni << "\n";
        cout << "giorgio: " << giorgio << ", " << &giorgio << "\n\n";
    }
};

class Widget {
public:
    // members
    int myInto;
    Gadget myGadget;
    // constructor
    Widget(int giov, int gior, int myo): myGadget(giov, gior), myInto(myo) {}
    // info
    void tellMe() {
        cout << "myInto: " << myInto << ", " << &myInto << "\n";
        myGadget.tellMe();}
    // getters: by value, by reference
    Gadget getGadget_val() {return myGadget;}
    Gadget& getGadget_ref() {return myGadget;}
};

int main() {
    Widget w{2,-3,77};
    cout << "defined new widget w at " << &w << "\n";
    w.tellMe();

    Gadget g1 = w.getGadget_ref();
    g1.giovanni--;
    w.tellMe();
    // does not change w: g1 is a copy of w.myGadget!!

    Gadget& g2 = w.getGadget_ref();
    g2.giovanni--;
    w.tellMe();
    // works: notice when we create g2 we use Gadget&

    Gadget g3 = w.getGadget_val();
    g3.giovanni--;
    w.tellMe();
    // does not change w, as expected

    // Gadget& g4 = w.getGadget_val();
    // does not even compile, as RHS is temp
}
```

This all makes sense, however the difference between `g1` and `g2` is subtle: it's only in the type we use. We could easily make that mistake and copy something without noticing (or viceversa). What if we don't want to allow copies? Or references? For this we can tinker with `Gadget`'s constructors.

## Constructors
Let's delete the copy and move constructors. We also need to delete `getGadget_val` as that implicitly uses those.

```C++
#include <iostream>
using std::cout;

struct Gadget {
    // members
    int giovanni;
    int giorgio;
    // constructor
    Gadget(int giov, int gior): giovanni(giov), giorgio(gior) {}
    Gadget(const Gadget&) = delete;
    Gadget(const Gadget&&) = delete;
    // a convenient method
    void tellMe() {
        cout << "giovanni: " << giovanni << ", " << &giovanni << "\n";
        cout << "giorgio: " << giorgio << ", " << &giorgio << "\n\n";
    }
};

class Widget {
public:
    // members
    int myInto;
    Gadget myGadget;
    // constructor
    Widget(int giov, int gior, int myo): myGadget(giov, gior), myInto(myo) {}
    // info
    void tellMe() {
        cout << "myInto: " << myInto << ", " << &myInto << "\n";
        myGadget.tellMe();}

    Gadget& getGadget_ref() {return myGadget;}
    // we had to eliminate the _val version
};

int main() {
    Widget w{2,8,77};
    w.tellMe();
    
    Gadget& g1{w.getGadget_ref()};
    g1.tellMe();

    // Gadget g2{w.getGadget_ref()};
    // does not compile: we deleted the copy constructor
}
```

As we can see in the last line, we can't make a copy as the copy constructor has been deleted. Just for fun, let's keep the move constructor deleted, but not the copy constructor. This should allow us to make copies, right? Well, as it turns out this code does not compile:

```C++
// does not compile!
#include <iostream>
using std::cout;

struct Gadget {
    // members
    int giovanni;
    int giorgio;
    // constructor
    Gadget(int giov, int gior): giovanni(giov), giorgio(gior) {}
    Gadget(const Gadget&&) = delete;
    // a convenient method
    void tellMe() {
        cout << "giovanni: " << giovanni << ", " << &giovanni << "\n";
        cout << "giorgio: " << giorgio << ", " << &giorgio << "\n\n";
    }
};

class Widget {
public:
    // members
    int myInto;
    Gadget myGadget;
    // constructor
    Widget(int giov, int gior, int myo): myGadget(giov, gior), myInto(myo) {}
    // info
    void tellMe() {
        cout << "myInto: " << myInto << ", " << &myInto << "\n";
        myGadget.tellMe();}
    // getters: by value, by reference
    Gadget getGadget_val() {return myGadget;}
    Gadget& getGadget_ref() {return myGadget;}
};

int main() {
    Widget w{2,8,77};
    Gadget g1{w.getGadget_val()};
}
// does not compile!
```

The reason being the copy constructor has been _implicitly_ deleted by our deletion of the move constructor. To remedy, we need to be a bit more explicit about the copy constructor. For example by making it default.

```C++
#include <iostream>
using std::cout;

struct Gadget {
    // members
    int giovanni;
    int giorgio;
    // constructor
    Gadget(int giov, int gior): giovanni(giov), giorgio(gior) {}
    Gadget(const Gadget&) = default;
    Gadget(const Gadget&&) = delete;
    // a convenient method
    void tellMe() {
        cout << "giovanni: " << giovanni << ", " << &giovanni << "\n";
        cout << "giorgio: " << giorgio << ", " << &giorgio << "\n\n";
    }
};

class Widget {
public:
    // members
    int myInto;
    Gadget myGadget;
    // constructor
    Widget(int giov, int gior, int myo): myGadget(giov, gior), myInto(myo) {}
    // info
    void tellMe() {
        cout << "myInto: " << myInto << ", " << &myInto << "\n";
        myGadget.tellMe();}
    // getters: by value, by reference
    Gadget getGadget_val() {return myGadget;}
    Gadget& getGadget_ref() {return myGadget;}
};

int main() {
    Widget w{2,8,77};
    Gadget g1{w.getGadget_val()};
}
```