wicklund@intellistor.com (Tom Wicklund) (02/23/91)
I've been looking at a few implementations of class "complex" recently
and wonder current C++ class members are sufficient.
A "simplistic" implementation of complex would look something like:
class complex {
protected:
double re;
double im;
public:
// constructors ...
complex operator+(complex c);
complex operator-(complex c);
.
.
.
};
The operators on complex values (+, -, *, /, etc) are defined as
member functions since they operate on the data type "complex".
However, in the implementations of complex I've seen I find that
member functions are not used, and it appears intentionally avoided
whenever possible.
The ATT C++ library (as documented in the C++ 2.0 library reference
manual, I don't have access to cfront or the actual library) they
define all complex operators as friend functions:
friend complex operator+(complex c1, complex c2);
friend complex operator-(complex c1, complex c2);
The GNU g++ library implements complex operators completely outside of
the class (they are not even friend functions) via:
Complex /* const */ operator + (const Complex& x, const Complex& y);
Complex /* const */ operator + (const Complex& x, double y);
Complex /* const */ operator + (double x, const Complex& y);
Complex /* const */ operator - (const Complex& x, const Complex& y);
Complex /* const */ operator - (const Complex& x, double y);
Complex /* const */ operator - (double x, const Complex& y);
These implementations bring up the questions:
1. Why aren't member functions used? In the ATT case my guess is
that the friend implementation allows automatic int/float
conversion to complex on either side of the operator. A member
function "operator +" won't convert the left hand argument
automatically. In the GNU case it looks like somebody decided to
implement these functions independent of class representation
(the implementation is in terms of public functions and a
constructor).
2. If member functions aren't powerful enough to implement a class
like complex, is the language lacking something? The whole
concept of friend functions seems to point to missing language
facilities.
jbuck@galileo.berkeley.edu (Joe Buck) (02/27/91)
In article <1991Feb22.190313.4936@intellistor.com>, wicklund@intellistor.com (Tom Wicklund) writes: |> I've been looking at a few implementations of class "complex" recently |> and wonder current C++ class members are sufficient. They are. Your example is a poor design of the "complex" class. In fact, friend functions are not needed at all. I recommend that you define class Complex something like this: class complex { private: double re; double im; public: // constructors complex() : re(0), im(0) {} complex(double r) : re(r), im(0) {} complex(double r, double i) : re(r), im(i) {} complex(const complex& c) : re(c.re), im(c.im) {} // assignment operators complex& operator=(const complex& c) { re = c.re; im = c.im; return *this; } complex& operator+=(const complex& c) { re += c.re; im += c.im; return *this; } // -=, *=, /= defined similarly }; inline complex operator+ (const complex& a, const complex& b) { complex tmp(a); return tmp += b; } // -, *, / defined similarly. NOTICE: operator+ is NOT a friend. It only accesses public operations of class complex. An approach like this is used in the Gnu library, and is advocated by Andrew Koenig in his tutorials. Notice the structure of the operator+ function above. Notice that it expresses the logical relation between the + operator and the += operator in a very clean way. In fact, it could be implemented as a template, once these are added to the language. On that glorious day one can write template <class T> inline T operator+ (const T& a, const T& b) { T tmp(a); return tmp += b; } and instantly, any class with a += operator has a corresponding + operator (see chapter 14 of E&S for a discussion of templates). |> These implementations bring up the questions: |> |> 1. Why aren't member functions used? In the ATT case my guess is |> that the friend implementation allows automatic int/float |> conversion to complex on either side of the operator. A member |> function "operator +" won't convert the left hand argument |> automatically. In the GNU case it looks like somebody decided to |> implement these functions independent of class representation |> (the implementation is in terms of public functions and a |> constructor). Your guess is correct; in both cases a symmetrical + operator results (it can add int, double, float, complex in any combination). The GNU case is doing it the right way. Member functions of an object are functions that operate on that object. + does not operate on a single object, it takes two arguments and produces an output that is a function of both and changes neither. It can be cleanly defined in terms of the += primitive. |> 2. If member functions aren't powerful enough to implement a class |> like complex, is the language lacking something? The whole |> concept of friend functions seems to point to missing language |> facilities. As the GNU case shows, friends are not needed at all. Some people think everything should be done with member functions. This is misguided, in my view. Member functions define the interface of a class. More complex operations that can be defined efficiently using class primitives shouldn't be member functions, in my view; they should be what Grady Booch calls "free subprograms". -- Joe Buck jbuck@galileo.berkeley.edu {uunet,ucbvax}!galileo.berkeley.edu!jbuck