A True C++ Complex Number Class
In CS1210 we saw a rudimentary complex number class (see complex_op.h, complex_op.cpp, and complex_op_app.cpp). It had some commendable features and some features which were uncommon to typical C++ style.
Good Features:
Uses the class construct
Provides a constructor
Provides binary operators: +, 1, *, and / to manipulate complex numbers
Uncommon Features:
Member variables (or attributes) are public
Operator functions are not part of the class (i.e. they rely on the public nature of the attributes)
Member function print is different from common C++ output (i.e., << operator)
In CS1220 we'd like to fix the uncommon features. Here's how we do it:
Public member variables:
This fix is easy, just insert the keyword private: in between the constructor and the attributes:
class
complex_number {
public:
complex_number(float real = 0.0, float imag =
0.0) {r = real; i = imag;}
private: // inserted to disallow non-member access to
attributes
float r;
float i;
};
However, this fix causes problems, because now the operator functions no longer work (remember they relied on being able to publicly access the attributes). See how to fix this problem in the next bullet below.
Non-class operator functions:
Our first though might be to simply make the operator functions members of the class. For example, we could do this with addition by changing the class definition as follows:
class
complex_number {
public:
complex_number(float real = 0.0, float imag =
0.0) {r = real; i = imag;}
complex_number operator+ (const complex_number &a, const complex_number &b);
private:
float r;
float i;
};
We would also need a corresponding change in the implementation to include scope resolution for the addition operation. For example (note the bolded change):
complex_number::complex_number operator+ (const complex_number &a,
const complex_number &b);
However, the binary operator + takes two parameters (that's why it's called binary). Although its not immediately obvious, as defined above the member function + takes three arguments: a and b are the second and third arguments. The first parameter (which we don't see in the parameter list) is the implict this argument that is passed to all member functions. So we have a choice to do one of two things, we can either:
Keep + as a member function an drop the unnecessary a parameter. The code is:
complex_number::complex_number operator+ (const complex_number &b) {
complex_number result;
result.r = r + b.r; //the "r" all by itself is the this->r (that is the implicit parameter)
result.i = i + b.i;
return result;
}
Make + a non-member friend function so it can keep both parameters and have the privileged access it needs to access the class attributes. The code is:
complex_number operator+ (const complex_number &a, const complex_number &b) {
complex_number result;
result.r = a.r + b.r;
result.i = a.i + b.i;
return result;
}
There is no consensus in the C++ community on which way (of the two alternatives above) is "better." Some like the first, because they like to avoid friend functions at all costs. Others like the second because it treats the complex numbers on the left and right sides of the + equally. This equal treatment comes in handy in the equation: x = 2.5 + y; // where x and y are complex numbers.
A more natural way to print:
Wouldn't it be nice to print complex numbers in the same way we print other built-in types. For example, when we want to print an integer x, we simply write the code: cout << x;
Suppose we tried the above output statement when x was a complex number? Well the "<<" is the insertion operator: it takes a output stream (the cout) on the left and something it knows how to print (like the int x) on the right. So all we have to do is create a new version of << that knows about complex numbers. Here's the code:
ostream& operator<< (ostream &out, const complex_number &b) {
out << b.r << "+" << b.i << "i";
return out;
}
Note: the function for the operator << must be a friend function. Why? Because the left side, that is the first argument to the binary operator << is an output stream. If the << was a member function, the first argument would be the implicit this parameter. And this would not be an output stream (it's would be complex number).
Now that we've discussed all the changes, here are the results (complex.h, complex.cpp, and complex_app.cpp)
The complex number implementation is cool!! We can declare variables of type complex_number and put them in equations and print them---just like any other build-in type. We can mix double and integer constants with our complex numbers (how does this work?). But what can't we do yet?
We can't read complex numbers from cin
We can't initialize complex numbers naturally, e.g.: x = 2+3i; // rather than x = complex_number(2, 3)
How can we fix this? Complete HW4.
HW4 sample test program (complex_test.cpp).