Can anyone give me some pointers?
After one week with Sams Teach Yourself C++ in 21 Days, it feels like I have the basics of C++ down: constants and variables, functions, some operators, loops,
switch statements etc. Object oriented programming was introduced surprisingly early (Day 6), and the memory discussion at the end of Day 5 took some time to digest, but other than that everything went smoothly.
In the second week, the book takes up a topic that I’ve found pretty hard: pointers and references. In this step, I’ll write about some of the things about pointers and references that confused me. It’s assumed that you already know the basics of pointers/references.
Pointers are introduced with some silly examples that doesn’t make much sense – why manipulate a variable using a pointer instead of directly assigning a value to the variable? Soon, however, the book tells you why pointers exist. (In this case, I think I would’ve preferred the other way round – first present the problem, then talk about the solution(s). It’s probably the way Accelerated C++ approaches the subject, although I haven’t come to pointers in that book yet).
If you’ve studied some C++, you have probably come across the stack and the free store (the heap). Local variables (and function parameters) are declared on the stack, and go out of scope when the function returns. Variables on the heap, however, remain until you free the memory manually or when the program ends.
In C++, you allocate space on the heap with
new returns a pointer, which is how you access data on the heap. I like the analogy in Sams Teach Yourself C++ in 21 Days: let’s think of the memory address as a telephone number, say, to the local pizza store. Since you eat pizza all the time, you program your phone to call that number when you press a special button. You don’t have to remember the phone number anymore, and you don’t need know where the pizzeria is located – you can still access it by pressing that button on your phone.
Now back to programming. You can think of the pizzeria as being on the heap (it’s “somewhere”). Isn’t it good that our phone has this handy button? You probably know where I’m going – that button is our pointer to the pizzeria. In a similar way, you can access variables on the heap just by using pointers!
Another use of pointers is to pass variables by reference to functions. A classic example is the
void swap(int a, int b) function. If you pass two variables to the function, you’ll notice that they aren’t swapped at all! This is because the function receives copies of the values, not the actual variables. One way to solve this problem is to pass pointers to the variables instead:
void swap(int *a, int *b). This way the function can access the original variables and do the swap.
Pointers vs References
If you want to use the
swap function from the previous section, you have to pass it the address of the variables (on the stack) using the address-of operator (
swap function body, you have to dereference the pointers with
int temp = *second;
*second = *first;
*first = temp;
Quite troublesome, isn’t it? (Here, we’ve even skipped to check if the pointer is a
null pointer). Fortunately, you can accomplish the same thing with references. A reference is an alias for an object. If we pass references to our function,
void swap(int &a, int &b), we can do everything the “normal” way since
b in the function body are aliases for the variables we pass into the function (and not copies!).
Seeing how easy it is with references, pointers feel kind of, well, pointless. So when should I choose to use pointers instead of references?
The rule of thumb seems to be:
Don’t use a pointer when a reference can serve the same purpose.
With that said, there are a few situations when you want to use pointers according to Johnny Bigert (Swedish):
- If it’s possible for the object to be
null. Sams Teach Yourself C++ mentions that some compilers support
nullreferences, but recommends to avoid them; instead, you should use a pointer, which can be assigned to
- If you want to change the object being pointed to. References can’t be reassigned, so you should use a pointer in this case.
“Most C++ books recommend references whenever possible, according to the general perception that references are “safer and nicer” than pointers. In contrast, we at Qt Software tend to prefer pointers because they make the user code more readable.”
The use of
If you look at the
swap example, you see a lot of
&. If the function prototype looks like
void swap(int &a, int &b);, should you also prefix the arguments with
& when you call the function?
This is how I thought in the beginning, and obviously it was not the right approach. What I did was to confuse the address-of operator with the reference operator, and the same for
*. The compiler can see the difference from the context, but how do you do it?
It’s very simple, really. You declare a pointer by writing the type followed by
* and finally followed by the pointer name, for example
Personally I think it makes more sense to think that
* belongs to
int, to declare a pointer to an
int. The problem is that if you write it like
int* pointer1, pointer2;
you would expect two pointers, but what you really get is only one pointer –
pointer2 is an
int. How the whitespace is placed varies from programmer to programmer.
When you use the dereference operator (
*), you don’t have a type in front of the
*, for example
It’s the same for
& when declaring references (
type &ref) and using it as the address-of operator (
Now back to functions. When looking at a function prototype, I now think: “what kind of arguments does it expect”? Let’s say it looks like this:
void increment(int *var);
The function takes a pointer to an
int. I can pass it a pointer, or I can use the address-of operator (
&) to pass it the address of an
int i = 1;
If the function takes a reference,
void increment(int &var);
I just think that the function, if properly written, will adjust my variable in some way.
There are times when you don’t want to modify the original variable, but still want to pass by reference. This has to do with performance – when passing by reference the function can access the original variables and doesn’t need to make a copy. When passing an
int, it won’t make a big difference; but when it comes to big classes, you can gain a lot by passing function arguments by reference.
What you want to do is to tell the function, “OK I give you access to my variable, but don’t you dare to touch it!”. You already know how to do the first part, and the second can be accomplished with the
const keyword. References are always “constant” (they can’t be reassigned), so
const together with a reference is always intended to make the object referred to constant:
int const &a =
; // correct
const int &b =
; // correct
int &const c = …; // not valid
as litb pointed out. However, this doesn’t apply to pointers:
const int * pointer1; // pointer to constant int - the value pointer1 points to can't be changed
int * const pointer2; // constant pointer to int - the object pointer2 points to can't be changed
Of course, you can combine the two:
const int * const pointer3; // the object pointer3 points to and the value can't be changed
The trick taught in Sams Teach Yourself C++ is to look to the right of
const. In the first example, the
int (pointed to) is constant; in the second,
pointer2 (the pointer) is constant.
Finally, just a few words about the cute
-> operator. If you want to access members of an object using a pointer, we would have to dereference the pointer first:
Since this is something we’ll do quite often, C++ provides the
-> operator for indirect access. That means that we can as well write it like this:
Pointers in Qt
-> being used a lot in Qt – in fact, almost all widgets (pushbuttons, labels etc.) are created on the heap in a way similar to this:
QPushButton *okButton = new QPushButton("OK");
Why aren’t they created on the stack? That’s something I wondered for a long time, and The Book of Qt 4: The Art of Building Qt Applications by Daniel Molkentin finally cleared it up for me. In C++, you have to remember to
delete objects on the heap. Qt makes memory management easier by providing a parent-child hierarchy for objects. All objects derived from the QObject class can benefit from this; when a parent is deleted, it also deletes all its children. If a child acts as a parent for some other widgets, it also deletes its children, and so it goes on until all descendants are deleted.
If I understand Daniel Molkentin correctly, objects have to lie on the heap to take advantage of Qt’s memory management. illissius commented that “that’s sort of backwards”, make sure to read his whole comment.
When you create a new object, you can specify the parent:
QVBoxLayout *mainLayout = new QVBoxLayout(&window);
In this case
window lies on the stack, since it’s the top-level widget.
When using some functions, for example
mainLayout->addWidget(okButton), the widget gets automatically added to a parent-child hierarchy. Here,
mainLayout‘s parent (
window) assumes parentage of
mainLayout is not a parent of
okButton, as one might think.
Phew, this was one quite long step. Remember, however, that there are much more to learn about pointers and references – I’ve only picked a few areas that I’ve found hard. For example, I skipped the topic of dangerous pointers entirely – something you shouldn’t do if you’re studying C++.
I hope to keep the next steps shorter, to only report my progress. I’ll blog again when I’ve finished my Sams Teach Yourself C++ book and started with some basic Qt (yay, screenshots?). Now, I’m waiting for the obligatory comment…