From: tmb@arolla.idiap.ch (Thomas M. Breuel)
Newsgroups: comp.lang.c++
Subject: Re: Reference counting for vectors, (Tony Hansen's book).
Date: 16 Aug 92 20:05:35 GMT
	<BRISTER.92Aug14110706@tirade.decwrl.dec.com>
	<TMB.92Aug14232208@arolla.idiap.ch>
	<BRISTER.92Aug14170213@tirade.decwrl.dec.com>
Reply-To: tmb@idiap.ch
Organization: IDIAP (Institut Dalle Molle d'Intelligence Artificielle
	Perceptive)
In-reply-to: brister@decwrl.dec.com's message of 15 Aug 92 00:02:13 GMT

In article <BRISTER.92Aug14170213@tirade.decwrl.dec.com> brister@decwrl.dec.com (James Brister) writes:

   On 15 Aug 92 03:22:08 GMT, tmb@arolla.idiap.ch (Thomas M. Breuel) said:
   > Using reference counting to get reference semantics on parameter
   > passing is very confusing indeed (sadly, this bad idea seems to
   > pervade the C++ literature).

   Perhaps if it's so pervasive then it's not such a bad idea.

   parameter passing was only a simple example, there are lots of cases where
   multiple objects need to keep track of a piece of data, and unless you're
   very sure about who is supposed to delete the data and when (non-trivial
   when the system gets complex), you can get yourself into a lot of trouble.
   Reference counting can save the day.

Memory management and reference/value semantics are very different
concepts (though implementationally closely related).

From the user's point of view, there is no difference in terms of
memory management between (correctly implemented) arrays with
reference semantics or arrays with value semantics. You don't have to
think about "who is supposed to delete the data" in either case.

In terms of behavior, there is, of course, a big difference.  Arrays
with reference semantics mean that any name that has been initialized
from, or assigned to, from some other such array object will be
aliased to the same piece of memory. Unexpected aliasing teds to lead
to difficult to trace bugs in programs.

Arrays with value semantics, on the other hand, behave just like
scalars and structures, which is what programmers expect and are used
to. The worst that can happen is that you incur a few redundant
copies, and if those happen to make a difference in terms of
performance (which is _very_ rare), you can find those places by
profiling.

Personally, I would prefer to be able to disallow assignment
("operator=(A&)") and copy constructor ("A(A&)") for my array classes
altogether. Copying is a potentially expensive operation, and hence
the user should ask for it explicitly. Aliasing (as you get it with
reference counting implementations of arrays) is a dangerous
operation, and hence the user should also ask for it explicitly.

Unfortunately, C++ forces you to use the same function (the copy
constructor) for passing parameters, returning values, and
initialization of variables. I have always considered this a
shortcoming, although I'm not sure that it is fixable.

You can get a simple template implementation of arrays with value
semantics via anonymous FTP from maya.idiap.ch:pub/tmb/array.shar.

   > If you truly want reference counting semantics, you can easily derive
   > it from objects with value semantics using a template class.

See below for an implementation. I wrote it mostly for fun. I find
reference counted semantics pretty useless, so I haven't tested it
much.

   > Copy-on-write is not really an option for classes like arrays that
   > require high-performance updates. You don't want to pay the price for
   > the memory fetch and conditional branch on each array store.

   If the array copies itself the first time it is written to, then you don't
   loose anything except on the first access, and with large data it would be
   better to have COW than to duplicate everything to protect against
   unintended changes.

This is not true. In general, you have to check a flag on every (not
just the first) modification to see whether the array has been copied.

As usual, you can take my advice or leave it. I have found arrays with
reference semantics to be a bother, and I recommend that people stay
away from them.

Whether you use copy-on-write to get value semantics or something else
makes little difference, except that copy-on-write has _some_ overhead
on every update. For numerical code, I have found that a "dumb"
implementation of arrays with value semantics is the better choice.

					Thomas.

================================================================

Counted.h

#ifndef __COUNTED__
#define __COUNTED__

template <class T>
struct Counted {
 private:
	int *count;
	T *object;
	void drop() {
		if(!count) return;
		(*count)--;
		if(*count<=0) {
			delete count;
			delete object;
			count=0;
			object=0;
		}
	}
	void acquire() {
		(*count)++;
	}
 public:
	Counted() {
		count=0;
		object=0;
	}
	Counted(T *object):object(object) {
		count = new int(1);
	}
	~Counted() {
		drop();
	}
	Counted(Counted &other) {
		count=other.count;
		object=other.object;
		acquire();
	}
	Counted &operator=(Counted &other) {
		drop();
		count=other.count;
		object=other.object;
		acquire();
		return *this;
	}

	T &operator*() {return *object;}
	T *operator->() {return object;}
	operator T() {return *object;}
	

	// do _not_ provide "operator T*()"
};

#endif

================================================================

A simple test program for the above:

extern "C" {
#include <stdio.h>
}
#include "Counted.h"

struct A {
	int val;
	A() { printf("created A@%p\n",this); }
	~A() { printf("destroyed A@%p\n",this); }
};

main() {
	Counted<A> x(new A);
	x->val = 42;
	printf("x@%p\n",&(*x));
	Counted<A> y(x);
	Counted<A> z;
	z=x;
	printf("z@%p\n",&(*z));
	printf("%d\n",z->val);
}