[Lumiera] TimelineZoomScale Question About Pointers

Ichthyostega prg at ichthyostega.de
Sun Oct 9 02:16:01 CEST 2011


Am 08.10.2011 23:15, schrieb Michael Fisher:
> this might seem odd but I'm trying to figure out why my code is 'working' as 
> opposed to 'not working'. I fiddled with *'s and &'s until it would build and
> run without a crash.

> It works fine (i think), I just don't understand why.

congrats, you seem to be an eager coder!


> Sorry for being such a pointer's newbie.  I just really, REALLY, want to 
> understand them....

> Also, how do I know when to use  ->'s instead of .'s  .  I'd much rather know
> what the heck I'm doing instead of winging it.  What's really confusing
> though to me is some objects are using both.  Totally weird to me.


Here you're looking into the abyss of history ;-)

C is a bit a shady language. It wasn't the most enlightened or expressive
language already when invented in 1971, but it was supposed to be close to
the hardware. At least it is consistently low-level and consequent in its
concepts.

Not so C++. It was an attempt, started roughly 20 years later, to sneak
in some more modern concepts and methods into the brains of a community
of die hard oldimer pragmatists, which clamped to their "high-level assembler".
In order to succeed with this piece of subversion, the new concepts were
layered on top of the old ones, leaving everything optional and combining
everything with everything, creating a huge messy pile of complexity.

Unfortunately this subterfuge was vastly successful, while most well
designed, well thought out modern languages are stuck within the
academic ivory tower. So that's all we've got, even today.


So we've to deal with several concepts mixed and combined here:

- the most simple thing are values or value objects. They are
  "right here", either in the local scope, as object members,
  or as global or static variables.

- next, we have pointers, which store the address of another
  variable. They can be used to refer to those indirectly,
  but also the address stored in a pointer can be manipulated,
  for example to get an array-like access to a table of values.

- then, we have a completely separate, independent concept: the reference.
  This is more high-level: it is an "alias to some other object", and
  what this actually means in the implementation is left to the compiler.
  You need to "bind" a reference immediately when it's defined, and from
  that point on, you use it as if it was the original *value* it refers to.

Syntactically this means, that values and references look alike,
while pointers utilise a different syntax for accessing objects.



> here's what I have. Trying to get a reference from TimelineViewWindow into 
> TimelineZoomScale

> timeline-zoom-scale.hpp

> added a.. TimelineViewWindow *timelineViewWindow

OK, so this declares a new member of the TimelineZoomScale object.
This member variable itself is a pointer, which can point to a
TimelineViewWindow object. I.e. it can hold the address of such an
object, and it can be changed to another address. This is exactly
what we want here.

> used in timeline-zoom-scale.cpp as

> void
> TimelineZoomScale::set_view_window(TimelineViewWindow& view_window)
> {
>   timelineViewWindow = & view_window;
> }

OK, so this defines a member function ("method") of TimelineZoomScale objects.
This function "set_view_window" takes a parameter *by reference* (i.e. an
alias). Whenever this function is called, the compiler will arrange for the
actual parameter to "refer somehow" to the given TimelineViewWindow instance.

In the body of this function, you take the *address* of this TimelineViewWindow.
You do this with the & -operator. This is perfectly legal, because a reference
is an alias and the compiler arranges that it behaves exactly like the original.
Thus you can take it's address as if you'd used the real thing.

And moreover, you assign this address to the member field
TimelineZoomScale::timelineViewWindow
This is the new pointer field you added. This is also perfectly legal, because
a pointer can be re-assigned any time and thus will from now on hold the address
of the TimelineViewWindow you used when invoking set_view_window().




> Gets called from timeline-panel.cpp  as

> zoomScale.set_view_window(timelineWidget->get_state()->get_view_window());

> It works fine (i think),
perfect, and exactly what we want.

Let's dissect this line too...
- we are in the scope of TimelinePannel
- this holds a member field of your new type ZoomScale,
  i.e. this member is a value "right here" and owned by TimelinePannel
- thus you're invoking the function "set_view_window" direclty with the
  '.' (dot operator)
- to get the parameter, you use the timelineWidget member field of
  TimelinePannel. This member variable is a "smart-pointer", which for
  the purpose of our discussion is equivalent to a plain simple pointer.
- thus, for invoking a function through this (smart)pointer, we first need
  to "dereference" that pointer (that means: use the address stored in the
  pointer to access the referred to target object). After this "dereferencing"
  we need to invoke the member function TimelineWidget::get_state
- now, C has a special syntax for combining those two steps: '->'
  (the arrow operator): "dereference a pointer and then access a member on
  the target". It would be possible (and probably more logical)
  to write those two steps explicitly:

  (*timelineWidget).get_state()

- similarily, the TimelineWidget::get_state member function returns a
  (smart)pointer to a TimelineState object, thus we use the arrow operator
  a second time to access the member function TimelineState::get_view_window()

- and now we encounter a minute, but very important detail: this Member function
  is defined as (timeline-state.cpp, 68)

  TimelineViewWindow&
  TimelineState::get_view_window()
  {
    return viewWindow;
  }

  please notice: this returns *a reference* (an alias to the member field
  TimelineState::viewWindow). If it wouldn't return a reference, but a value
  then your code wouldn't work, because then your code would take the address
  of an temporary value object. But, fortunately it returns a reference, and
  thus everything is fine.


> I just don't understand why.  since i'm passing in TimelineViewWindow& 
> view_window, why doesn't it work with timelineViewWindow = view_window 
> (without the &)?

Without the '&' (take-the-address operator), you would try to assign
a TimelineViewWindow object by value to a TimelineViewWindow-pointer.
Recall, a reference is an alias. When you write it, the compiler treats
that statement as if you were able to access the original thing referred to.

Thus, without the addresof-operator, you write a simple value assignment.
But the target of the assignment can't hold that value, because it's just
an pointer (an address). That is 32bit or 64bit, depending on your platform,
while a complete TimelineViewWindow object is obviously way larger.
Fortunately the compiler complains at that point and prevents you from
doing any damage. That is the benefit of strict typing.

> Why does it need to "take the reference of" a variable that is already a 
> reference?  I just don't get that.

Because the language designers ran out of grippy symbols, and thus choose
to overload the '&' symbol with two different meanings, depending on the
usage context.

- when used postfix of an Type name, than it means "this is a reference"
- when used prefix to a value or variable or expression, it means
  "take the address of what follows"

This is a striking example of bad language design, as those two concepts
are neither completely independent, nor logically equivalent. This is
bound to confuse *every* newbie, and it was tremendously successful
in making student's lives a misery.


> Also, how do I know when to use  ->'s instead of .'s

As elaborated above, it depends on what is left of the operator
- when it's a value (or a value aliased through a reference) then use '.'
- when it's an address (pointer) then you need to dereference with '->'


hopefully I've not freaked you off completely by now ;-)

Cheers,
Hermann







More information about the Lumiera mailing list