A question of pointer initialization

M

manuhack

In Chapter 6 of Eckel's thinking in C++, there is an example:


//: C06:Stack3.h
// With constructors/destructors
#ifndef STACK3_H
#define STACK3_H

class Stack {
struct Link {
void* data;
Link* next;
Link(void* dat, Link* nxt);
~Link();
}* head;
public:
Stack();
~Stack();
void push(void* dat);
void* peek();
void* pop();
};
#endif // STACK3_H ///:~

//: C06:Stack3.cpp {O}
// Constructors/destructors
#include "Stack3.h"
#include "../require.h"
using namespace std;

Stack::Link::Link(void* dat, Link* nxt) {
data = dat;
next = nxt;
}

Stack::Link::~Link() { }

Stack::Stack() { head = 0; }

void Stack::push(void* dat) {
head = new Link(dat,head);
}

void* Stack::peek() {
require(head != 0, "Stack empty");
return head->data;
}

void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}

Stack::~Stack() {
require(head == 0, "Stack not empty");
} ///:~

I don't understand why the constructor of Stack sets head =0. head is
a pointer to the struct Link, so setting the address to zero can be
dangerous right?
 
H

Howard

manuhack said:
In Chapter 6 of Eckel's thinking in C++, there is an example:
I don't understand why the constructor of Stack sets head =0. head is
a pointer to the struct Link, so setting the address to zero can be
dangerous right?

Yes, it is dangerous, if you use it. But it's the standard method of saying
"I'm not pointing at anything".

The point is, in the constructor, you don't yet have anything to point to.
Only after something is added to the stack is there anything there. Notice
that the pop() function returns 0 if head is 0, instead of trying to access
the data.

Look up "NULL pointer" in your favorite book for more info.

-Howard
 
H

Howard

manuhack said:
In Chapter 6 of Eckel's thinking in C++, there is an example:



void* Stack::peek() {
require(head != 0, "Stack empty");
return head->data;
}

By the way, above is one thing I don't really like about the design. He's
got a "require" statement which (I assume) checks that head is not 0 when
calling peek, but there's no function isEmpty() or any other way for someone
to ask if the stack is empty (since head is a private variable). If peek is
going throw an exception when called on an empty stack (or whatever
require() does when the condition fails), then there really should be a
boolean function which tells you whether the stack is empty or not, so that
you can check if it's empty before you attempt to peek at the data member of
its "head" item.

-Howard
 
S

Salt_Peter

manuhack said:
In Chapter 6 of Eckel's thinking in C++, there is an example:


//: C06:Stack3.h
// With constructors/destructors
#ifndef STACK3_H
#define STACK3_H

class Stack {
struct Link {
void* data;
Link* next;
Link(void* dat, Link* nxt);
~Link();
}* head;
public:
Stack();
~Stack();
void push(void* dat);
void* peek();
void* pop();
};
#endif // STACK3_H ///:~

//: C06:Stack3.cpp {O}
// Constructors/destructors
#include "Stack3.h"
#include "../require.h"
using namespace std;

Stack::Link::Link(void* dat, Link* nxt) {
data = dat;
next = nxt;
}

Stack::Link::~Link() { }

Stack::Stack() { head = 0; }

void Stack::push(void* dat) {
head = new Link(dat,head);
}

void* Stack::peek() {
require(head != 0, "Stack empty");
return head->data;
}

void* Stack::pop() {
if(head == 0) return 0;
void* result = head->data;
Link* oldHead = head;
head = head->next;
delete oldHead;
return result;
}

Stack::~Stack() {
require(head == 0, "Stack not empty");
} ///:~

I don't understand why the constructor of Stack sets head =0. head is
a pointer to the struct Link, so setting the address to zero can be
dangerous right?

Thats good practice. The rule is always initilize all your variables.
That may sound like a ridiculous rule but the alternative is leaving
that pointer pointing to a residual value that appears to be a valid
pointer (even though it obviously is not). One would implement the ctor
like so:

Stack::Stack() : head( 0 ) { }

In other words: your empty Stack does not have a head yet.

While Eckel's code is slightly outdated his book is an excellent source
and a great learning experience.
 
M

manuhack

Thats good practice. The rule is always initilize all your variables.
That may sound like a ridiculous rule but the alternative is leaving
that pointer pointing to a residual value that appears to be a valid
pointer (even though it obviously is not). One would implement the ctor
like so:

That means one has to check whether the pointer is 0 or not before
doing something on it otherwise it would generate run-time error,
right?
While Eckel's code is slightly outdated his book is an excellent source
and a great learning experience.

Yes, Eckel's book is definitely a good read.
 
H

Howard

manuhack said:
That means one has to check whether the pointer is 0 or not before
doing something on it otherwise it would generate run-time error,
right?

That depends on the code, really. Depending on your design, you may never
have a situation where you need to ask, because you'll only be using the
pointer if you already know it's not NULL (0). But if you're writing more
general-purpose code, then you might want to check if it's NULL before using
it.

Imagine the alternative, though. If you didn't set it to NULL in the
constructor (and whenever you've just deleted it) , and the code was written
so that you didn't know in advance whether it was valid to use or not, how
would you determine if it was ok to use? A valid pointer value might be
anything, other than 0. So the value 0 is the easiest way to know that the
pointer shouldn't be used.

(Other options might include a flag that tells you if it's ok to use, or in
the case of that Stack class, an isEmpty function which tells you whether
there is anything in the stack.)

-Howard
 
M

manuhack

Imagine the alternative, though. If you didn't set it to NULL in the
constructor (and whenever you've just deleted it) , and the code was written
so that you didn't know in advance whether it was valid to use or not, how
would you determine if it was ok to use? A valid pointer value might be
anything, other than 0. So the value 0 is the easiest way to know that the
pointer shouldn't be used.

Got it. Thanks a lot.
 
S

Salt_Peter

manuhack said:
That means one has to check whether the pointer is 0 or not before
doing something on it otherwise it would generate run-time error,
right?

Not really. Suppose you accidentally forgot to set that pointer. And
suppose you are far into your project and a bug suddenly appears. If
the ctor presets the pointer to null, you know that whatever bug is
generated will not be associated with a null pointer since any
diagnostic your compiler might generate would have flagged that
condition clearly.

I've seen guys attempting to debug code for weeks over an issue as
trivial as this. Unlike other languages, C++ lets you define what a
default object looks like and behaves. If you as a programmer take the
simple, basic steps to make debugging code simpler, you and any client
programmer will detect errors immediately. The same logic can be
applied to any variable, including user-types.

There is no need to start checking every single pointer you come
accross. That isn't the goal. The goal is that if do forget to set the
pointer, the compiler will generate a very precise, unmistakeable
error.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top