Contributor: DJ MURDOCH               


>The solution I use For dynamic Objects (I don't have any Complex code) is
>to keep a counter in each matrix Record; every Function decrements the
>counter, and when it reaches 0, disposes of the Object.  if you need to
>use an Object twice, you increment the counter once before using it.

> if you allocate an Object twice, how do you get the first address back into
> the Pointer Variable so it can be disposed?   I must not understand the
> problem.  if I do:

> new(p); new(p);

> Unless I save the value of the first p, how can I dispose it?  And if I
> save it, why not use two Pointer Variables, p1 and p2, instead?

You're right, there's no way to dispose of the first p^.  What I meant is
something like this:  Suppose X and Y are Pointers to matrix Objects.  if I
want to calculate Z as their product, and don't have any need For them any
more, then it's fine if MatMul disposes of them in

  Z := MatMul(X,Y);

In fact, it's Really handy, because it lets me calculate X Y Z as

  W := MatMul(X, MatMul(Y,Z));

The problem comes up when I try to calculate something like X^2, because MatMul
would get in trouble trying to dispose of X twice in

 Y := MatMul(X, X);

The solution I use is to keep a counter in every Object, and to follow a rigid

 1.  Newly created Objects (Function results) always have the counter set to

 2.  Every Function which takes a Pointer to one of these Objects as an
     argument is sure to "touch" the Pointer, by passing it exactly once to
     another Function.  (There's an exception below that lets you pass it more
     than once if you need to.)

3.   if a Function doesn't need to pass the Object to another Function, then
     it passes it to the special Function "Touch()", to satisfy rule 2.
     Touch checks the counter; if it's zero, it disposes of the Object,
     otherwise, it decrements it by one.

4.   The way to get around the "exactly once" rule 2 is to call the "Protect"
     Function before you pass the Object.  This just increments the counter.

5.   Functions should never change Objects being passed to them as arguments;
     there's a Function called "Local" which makes a local copy to work on if
     you need it.  What Local does is to check the counter; if it's zero,
     Local just returns the original Object, otherwise it asks the Object to
     make a copy of itself.

For example, to do the line above safely, I'd code it as

  Y := MatMul(X, Protect(X));

MatMul would look something like this:

Function MatMul(Y, Z : PMatrix) : PMatrix;
  result : PMatrix;
  { Allocate result, fill in the values appropriately, then }
  MatMul := result;

The first Touch would just decrement the counter in X, and the second would
dispose of it (assuming it wasn't already protected before the creation of Y).

I've found that this system works Really well, and I can sleep at night,
knowing that I never leave dangling Pointers even though I'm doing lots of
allocations and deallocations.

Here, in Case you're interested, is the Real matrix multiplier:

Function MProd(x, y : PMatrix) : PMatrix;
{ Calculate the matrix product of x and y }
  result  : PMatrix;
  i, j, k : Word;
  mp      : PFloat;
  if (x = nil) or (y = nil) or (x^.cols <> y^.rows) then
    MProd := nil
    result := Matrix(x^.rows, y^.cols, nil, True);
    if result <> nil then
      With result^ do
        For i := 1 to rows do
          With x^.r^[i]^ do
            For j := 1 to cols do
              mp := pval(i,j);
              mp^ := 0;
              For k := 1 to x^.cols do
                mp^ := mp^ + c[k] * y^.r^[k]^.c[j];
    MProd := result;

As you can see, the memory allocation is a pretty minor part of it.  The
dynamic indexing is Really ugly (I'd like to use "y[k,j]", but I'm stuck using
"y^.r^[k]^.c[j]"), but I haven't found any way around that.