views:

128

answers:

3

Write an overloaded operator+ function so that two instances of the quadratic class can be added together as in the following code:

quadratic y1 = quadratic (1.0, -5.0, 7.0);
quadratic y2 = quadratic (-3.0, -2.0, 10.0);
quadratic y3;
double result;

y3 = y1 + y2;
result = y1.evaluate (10.0);
cout << result <<endl;

To help you out, here's the implementation for two of the number functions:

double quadratic:: evaluate (const double x) {
return ((a * x * x) + (b * x) + c);
}

void quadratic::getCoefficients (double &A, double &B,
double &C) {
A = a; B = b; C = c;
}

Desperately seeking a guru's help! Any help would be very much appreciated!

A: 

Well, from the provided functions it's obvious that quadratic has (at least) three member variables, a, b, and c:

class quadratic
{
public:
//...
private:
  double a, b, c;
};

In addition, we can infer that quadratic has a constructor that takes 3 doubles, and an operator+:

class quadratic
{
public:
  quadratic(double A, double B, double C) {/*...*/}
  quadratic operator+(const quadratic &other) {/*...*/}
  //...
private:
  double a, b, c;
};

Which part of the remaining implementation are you having trouble with?

I would overload the `+=` operator to implement addition, then overload the `+` operator in terms of the overloaded `+=` operator.
In silico
so would the function should be `quadratic operator +(double A, double B, double C)`?
HollerTrain
@HollerTrain No. First of all, the operator+ can only take one parameter (at least that's true if you make it a member function.) Second, look at the example code: y3 = y1 + y2. + there is acting on the "quadratic" object y2; there are no doubles at all. Therefore, you must write a function that takes as its parameter a quadratic object.
oh, so my y1 and y2 are actual objects being added together in my operator function?
HollerTrain
Yes, that's right.
but in a function how am i to know what objects are going to be added together if this is before actual implementation of the overloaded operator? that is the main part of this entire question i'm still unclear about
HollerTrain
A: 

The simple approach is using a bit of wishful thinking: just think that you do have the two objects before writing the addition operation.

Really, what goes on is that when you are defining the operation you are writing an algorithm that will later be applied. At the place of call, when some user types a+b, a and b will have to be valid instances of the type that you want to add. While defining the operation, the arguments to the function or method represent those to valid instances that will exist later.

Since this is homework, I will follow a different example. Consider writing a vector2d class that contains two doubles representing x and y coordinates.

class vector2d {
public:
   //...
private:
   double x,y;
};

And you want to offer some operations that can be applied to vector2d objects. You can define operator+= as a way of storing in a vector the result of adding that same vector with a second one:

class vector2d {
public:
   //...
   vector2d& operator+=( const vector2d& rhs )
   {
      x += rhs.x;
      y += rhs.y;
      return *this;
   }
};

For convention, we return a reference to a vector2d from operator+=. We operate both on the object that we are calling the operation (this is a member function, the *this object) and on a second object that we take as parameter. Notice that at this point there is no single object created in the program, we are just defining the operation, not the operands. The right hand side argument rhs is handled through a constant reference (we get a reference to the object that we promise not to change). The actual operation is simple, add each coordinate separately, and because the convention is returning a reference to the modified object, we return *this.

We have defined how the operation is performed in terms of two objects, and now we can use it:

int main() {
   vector2d a( 5, 10 );    // assume that there is a constructor that 
                           // takes 2 doubles in the ellipsis above...
   vector2d b( 2.5, 7.5 );
   a += b;
}

Now, to be able to make the call, the user needs to have two objects. To define the operation we do not actually need instances of the objects, we abstract that by means of the parameters. But to actually use the operation we do need the objects on which to operate. At this point they are actually created and named a and b. Then the user calls the operation: a += b. As it is a member method that call gets translated by the compiler into some less beautiful: a.operator+=(b), that is, it will call the member method operator+= on the object a passing b as argument.

To provide a proper addition operation, we can write a free function. The addition does not apply to any of the two arguments, but rather creates a third argument so it kind of makes sense that it is not a member method.

vector2d operator+( vector2d lhs, const vector2d & rhs )
{
   lhs += rhs;
   return lhs;
}

This implementation is idiomatic (a common pattern). Once we have operatorX= implemented we can implement operatorX in terms of the previous. Now the trickery: we take the first argument by value. That way, the compiler makes a copy for us lhs inside this function is not the first argument of the addition, but a copy of that. Then we use the existing operation to modify that local copy and we return the result to the user. The return object is also by-value.

The usage is similar:

int main() {
   vector2d a( 5, 10 );
   vector2d b( 2.5, 7.5 );
   vector2d c = a + b;
}

At the place of call, the a+b gets translated to operator+( a, b ) since it is a free function. Then because the first argument is passed by value, a copy of a is made and used as lhs in the function. The second argument is passed by reference and we promise not to change it (thus const). The result of the operation is stored in c.

David Rodríguez - dribeas
A: 

So.... you want to declare this function?

quadratic operator+(const quadratic& LHS, const quadratic& RHS);

Implement it as?

quadratic operator+(const quadratic& LHS, const quadratic& RHS)
{
    double LHSCoefficients[3];
    LHS.getCoefficients(LHSCoefficients[0], LHSCoefficients[1], LHSCoefficients[2]);
    double RHSCoefficients[3];
    RHS.getCoefficients(RHSCoefficients[0], RHSCoefficients[1], RHSCoefficients[2]);
    return quadratic(LHSCoefficients[0] + RHSCoefficients[0], LHSCoefficients[1] + RHSCoefficients[1], LHSCoefficients[2] + RHSCoefficients[2]);
}

Simple enough and it doesn't need to be a method, static method, or friend function. Mind you, quadratic::getCoefficients and quadratic::evaluate really should have constant protection on the invoking object. Also, you may very well just have methods like inline const double& quadratic::getA() const for individual components. This has likely less memory and processing overhead and does not allow the component to be modified, though it returns by reference.

Sion Sheevok