When I want to use an object provided by someone else, I have two choices: I can either request a copy of the object, or I can request a description on how to access the original object, called a reference.
Handing out a copy protects the original. It is therefore a very safe procedure. On the other hand, if I want to modify the original, I have to somehow merge the copy back into the original, which can be a very complicated procedure.
References makes changing the original easy. So easy in fact, that you have to worry about several people changing your object at the same time. References are inherently dangerous, and you have to take measures to protect your original.
Let's see this in action. We continue with last lecture's complex number example, and define a function that will multiply two complex numbers. Let's write this in C, where references are implemented as pointers:
struct complex {double x,y; }; void mult(struct complex op1, struct complex op2, struct complex* result) { result->x = op1.x * op2.x - op1.y * op2.y; result->y = op1.x * op2.y + op1.y * op2.x; }As you can see, we are passing 3 parameters to the mult() function. op1 and op2 are passed by value, meaning that the function receives a copy of the original structures. result is a reference, passed as a pointer to a struct complex.
Let's use this function in a main program:
main() { struct complex a = {1.0, 3.0}; mult(a,a,&a); printf("a*a = %e + %e*i\n", a.x, a.y); }The output will be:
a*a = -8.000000e+00 + 6.000000e+00*iNow let's suppose that we want to save the cost incurred in copying the two complex numbers, and use references everywhere. The program would look like this:
#include <stdio.h> struct complex {double x,y; }; void mult(struct complex *op1, struct complex *op2, struct complex *result) { result->x = op1->x * op2->x - op1->y * op2->y; result->y = op1->x * op2->y + op1->y * op2->x; } main() { struct complex a = {1.0, 3.0}; mult(&a,&a,&a); printf("a*a = %e + %e*i\n", a.x, a.y); }The result is not what we want:
a*a = -8.000000e+00 + -4.800000e+01*iThe reason is that in the first line of the mult() function, we are modifying our input data before we are finished using it. The dangerous thing is that the provider of this mult() function probably didn't anticipate the slightly unnatural usage in the main program (exercise: fix the mult() function without changing the call interface).
In C++, references are a distinct entity. You no longer need to use pointers to implement references. This has several advantages:
Note that within the code, you can no longer distinguish what is a reference and what is a copy. Therefore, it makes sense to use the following convention:
In essence, the above examples are bad code. Do as I say, not as I do...
Previous Idiom | Next Idiom | About Idioms
If a function wants to return a value, then use return to return it as a function value. Always pass parameters as copies. If a function wants to return several values, consider grouping those values into a structure (that can be returned), or splitting the function up into several small functions that each return part of the requested values. Note that functions that want to modify a value should be implemented as member functions of that value.
Previous Section | Next
Section | Contents
Basic Constructors
Let's review how variables get created, where they can live and who
can see them:
Previous Idiom | Next Idiom | About Idioms
Do not use global variables. Keep the internal representation of your data hidden from the users of your data. Otherwise, you are committed to maintaining the same datastructure forever. Use functions to access your data.
Previous Section |
Contents
Operators
Now we have the tools to understand one of the neatest constructions
in C++: operators. In standard C (and in most other
programming languages), we have to use often clumsy function calls to
express operations that can be written quite naturally with
operators. Instead of relying on (English) function names we can rely
on the (internationally) agreed meaning of operators. So, +
would always mean the addition or gathering of stuff. +=
would imply some sort of accumulation, * the composition of
stuff, -> the following or resolving of some sort of link,
etc...
Click here to look at a directory containing code files of a program to calculate eigenvalues using a novel algorithm. Have a look at the complex vector class and the complex matrix class definitions. Observe how simple a fairly complicated procedure looks like:
Previous Idiom | Next Idiom | About Idioms
Overloading the standard meaning of operators is a very powerfull tool to make programs simpler and shorter. Avoid abusing this feature by keeping to the natural meaning of the operator:
It is a good sign if you are actually using the operator in it's original meaning inside the implementation of the operator.
- + - * / etc...
- Use for math and math-like operations;
- == != < <= > >=
- Use for comparing things;
- []
- Use for indexing;
- ()
- Use only on types with one single valid operation;
- ->
- Use for resolving references ("smart" pointers);
Note that overloading does not change the syntactic properties of the operator. Binding and precedence remain unchanged.
At this point, I do not expect you to understand everything in those files, but the feeling should get through. As we go along in this course, more and more pieces will fall together.