[comp.lang.c++] efficiency affecting class interface

wrl@apple.com (Wayne Loofbourrow) (09/30/89)

I'm relatively new to C++, but am very used to C.  I'd like to
dive in and write programs in an object oriented fashion, but am
concerned about efficiency.

Ideally, efficiency concerns would not effect the design of the
interface to a class, but only its implementation.  However, here
is a case in point where it seems to.

Does anyone know of a better way?  Here's the situation:


People seem to have no trouble defining operators like this:

complex operator+(complex c1, complex c2)
{
    return complex(c1.re + c2.re, c1.im + c2.im);
}

...and using it like this:

complex a,b,c;
// ...
a = b + c;

One might be concerned about the creation of a temporary inside
the function, a copy to a temporary outside the function, and an
explicit copy into a.  However, complex objects are fairly fast
to copy and create so why worry?  Besides inlining would help.

Ok.  But now lets say you've got a more complicated beast with
lots of pointers chaining off of it.  For example, a graph object
implemented with various linked lists (say vertex lists).

Now, to be consistent with the way complex objects work, I want
assignment to make a copy of the graph.  That way I can modify
the original without affecting the copy.

So then I write:

graph operator+(graph g1, graph g2)
{
    graph t;
    // compute t from g1 and g2.
    return t;
}

This might compute a new graph that is the union of the two.

But now, when I say:

graph a,b,c;
// compute b and c
a = b + c;

All sorts of copying is going on, and each time an entire graph is
created (zillions of allocations and all).

From what I can tell, the best I can hope to do is:

inline graph operator+(const graph& g1, const graph& g2)
// ... same as before

which will still involve copying of large objects (from the
temporary created in computing (b+c) into a).

A possible solution is to define either:

void graph::operator+=(const graph& g)

or

void addgraph(graph& a, const graph& b, const graph& c)

but either of these are awkward to use and preclude using the add
operation in expressions.

Anyone have a good way get what I want?:
...convenient (b+c) style notation but without the copying
inefficiency.

Wayne Loofbourrow
Advanced Technology Group
Internet: wrl@apple.com

ttwang@polyslo.CalPoly.EDU (Thomas Wang) (09/30/89)

wrl@apple.com (Wayne Loofbourrow) writes:
>Ok.  But now lets say you've got a more complicated beast with
>lots of pointers chaining off of it.  For example, a graph object
>implemented with various linked lists (say vertex lists).

>graph a,b,c;
>// compute b and c
>a = b + c;

>All sorts of copying is going on, and each time an entire graph is
>created (zillions of allocations and all).

When you have BIG objects it makes sense to have a 'handle' class which
manages copying.  The 'handle' class would be very small, and contains a
real pointer to the 'graph' object.  So when a handle_graph object get
copied, it takes very small CPU time.  Then you make all the functions to
operate with the handle class.

class handle_graph
{
  graph* real_ptr;
  int    ref_count;
public:
  graph* ptr() { return real_ptr; }
  ptrcopy(handle_graph&, handle_graph&);
  fieldcopy(handle_graph&, handle_graph&);
};

This is reference counting.  With a set of class templates, the handle
definition can be generated semi-automatically.  I am in the process of
writing such a beast.

>Wayne Loofbourrow

 -Thomas Wang ("This is a fantastic comedy that Ataru and his wife Lum, an
                invader from space, cause excitement involving their neighbors."
                  - from a badly translated Urusei Yatsura poster)

                                                     ttwang@polyslo.calpoly.edu