Many students and programmers, even professionals, find declarations in C++ very confusing. In fact, many professionals propagate false information and then give (reasonable) advice based on the false information.

The following was written as an answer to a question on Piazza, and so it is not all that thorough and could benefit from additional text and examples. However, even in its current form, I hope that it proves helpful!


In C and for all but reference variables in C++, the rule for declarations is that you show how to use a variable to get a simple type (int, float, char, bool, a struct/union/enum, class). A declaration is NOT a type followed by a variable. You show C++ how to use a variable to get something it knows about.

            int n;
            int na[...];
            int nf();
            int *np;
            int &nr = n;
          

int n; means n is a variable that can hold int values, it is an integer variable.

int na[...]; means that na is a variable that you can subscript (na[i]) to get an integer; na contains an array of int variables.

int nf(); means nf is a thing you can call to get an int; it holds a function that returns an int.

int *np; means you can dereference np (*np) to get an int; np is a variable that can hold a pointer value that is the address of a variable containing an int.

Nice and consistent so far! Show C++ how you intend to use the variable to get to a thing of the named type.

int &nr = n; means that nr is a reference to another variable containing an int. Because a reference by definition is an alias for another variable, you can't have one that is uninitialized. This example now says that n and nr both refer to the same variable, the same place in memory! You cannot change what a reference refers to after it's set. It is just a name for some other variable.

A reference parameter is initialized when a function is called. That is, in int frob(Snail &gary);, every time the function is called, the caller has to call the function with a variable containing a Snail instance, and then inside frob, gary will will refer to that exact same variable (place in memory).

So, you can write n = frob(pink) if pink is a variable that holds a Snail.
Pause. This also works:

            Snail *cargo = new Snail;
            ...
            n = frob(*cargo);
            ...
          

Inside frob, gary will be another name for the Snail pointed to by cargo. This means that &gary inside frob, the address of the Snail object stored in gary, will be equal to the contents of cargo in the calling function!

That is what is going on in the assignment operator. this is a parameter/local variable that contains the address of the instance the member function was called on. The variable I usually call rhs is a reference to the variable on the right of the assignment. So, we check whether something is assigning to itself thus: if &rhs == this