views:

462

answers:

8

Suppose we want two constructors for a class representing complex numbers:

Complex (double re, double img)  // construct from cartesian coordinates
Complex (double A, double w) // construct from polar coordinates

but the parameters (number and type) are the same: what is the more elegant way to identify what is intended? Adding a third parameter to one of the constructors?

+30  A: 

It is better to add static methods with appropriate names and let them to create the objects.

static Complex createFromCartesian(double re, double img);
static Complex createFromPolar(double A, double w);
Mykola Golubyev
you can, of course, also have the constructor defer to one of these as the 'default' representation
jk
Ok, this is some sort of factory methods. It's the same solution Naveen points. The solution by Neil will work, but it implies writing two more classes.
cibercitizen1
@cibercitizen1 You will probably find you need those classes anyway. They can for example be used for overloading Complex member functions.
anon
@Neil: I doubt that it is a good Idea to have additional +-/* like Complex + Polar, Complex + Cart, and so on.
Mykola Golubyev
@Mykola: If it makes sense to have Complex + Complex, and it makes sense to make a Complex from a Polar, then it must make sense to have Complex + Complex(Polar), which means it must make sense to have Complex + Polar.
Bill
anon
I'd prefer a nonmember version: `Complex c = Polar(A, w);
jalf
+23  A: 

You can't have two constructors (or any functions) with the same signatures. The best solution is probably to create classes for your coordinate types and overload on those. For example:

struct CartCoord {
    CartCoord( double re, double img ) : mRe(re), mImg(img) {}
    double mRe, mImg;
};

struct PolarCoord {
    PolarCoord( double a, double v ) : mA(a), mV(v) {}
    double mA, mV;
};

Then your constructors become:

Complex( const CartCoord & c );
Complex( const PolarCoord & c);

In use:

Complex c( CartCoord( 1, 2 ) );

You can also use them with overloaded operators of the Complex class. For example, assuming you have a binary + operator for the class define as:

Complex operator+( const Complex &, const Complex & );

then:

Complex a( CartCoord( 0, 0 ) );
Complex b = a + PolarCoord( 1, 1 );

As we have a conversion constructor from PolarCoord to Complex, this will be used in the + expression. This is more natural (IMHO) than calling static functions to create the temporary..

This is an example of Koenig's dictum (or at least a version of it) - whenever faced with a difficult problem, introduce a new level of classes to solve it.

anon
This is the correct answer. Why downvotes?
chappar
I had the same answer, but this one is worded better. +1
seanhodges
Koenig's dictum? I thought Grady Booch said "Every problem in computer science can be solved by adding one more level of indirection".
duffymo
@duffymo Lots of people have said something similar - the Koenig version is specifically about classes.
anon
Thanks for the education, Neil.
duffymo
Alternatively, you could create a class for `Angle` and have one constructor accept `(double, double)` and one accept `(double, Angle)`. Angles have completely different properties than doubles, so that makes more sense to me (of course, you could do both).
Peter Alexander
It is a good idea about + polar (now I realized). I am in Java world now, and don't think in +-/* directions anymore.
Mykola Golubyev
@duffymo: For Koenig's dictum to really satisfy Booch's, it should read "whenever faced with a difficult problem, apply Booch's comments, using classes". It should also be noted that every level of indirection in computer science creates a problem ;-)
Steve Jessop
@Steve - Oh, so you've seen my code.... 8)
duffymo
@Steve @duffymo See http://stackoverflow.com/questions/2057503/does-anybody-know-from-where-the-layer-of-abstraction-layer-of-indirection-qu for an alternative origin.
anon
I would like to explain why I accepted Mykola's answer. From a design point of view, having two classes PolarCoord and CartCoord gives them some sort of "existence" they don't deserve. The real class is Complex: from the same complex c you can fetch real(c) and img(c) (cart coords) or amplitude(c) and angle(c) (polar coords) but the object is the same: cart coords polar coords are two facets of the same thing, and not two different objects with its own class.
cibercitizen1
@cibercitizen1 See if you feel the same in 6 months time :-)
anon
+2  A: 

You can't - if the method signatures are the same, you're stuffed.

Of course, you could always create a subclass for each type of coordinate system, but that's far from ideal - especially if you later find a good reason to want to subclass Complex for other reasons...

Martin Milan
Just noticed Mykola Golubyev's answer - Go with that. Have one type on your constructor, but provide a factory method for the other. What was I thinking??? Must be Monday.
Martin Milan
+1  A: 

I don't know if it is good practice in C++ but I would name these two methods differently, at least for a maintainability point of view.

mouviciel
but it is a constructor, you can not name them differently.
Naveen
I suppose you know constructors can't have two names. Are you suggesting having two factory methods?
cibercitizen1
I just suggest that in general when two methods have different behaviours, they should make it clear through their name or arguments. Other answers give implementation details better than I could do.
mouviciel
+7  A: 

Use the named constructor idiom described here

Naveen
+2  A: 

Instead of having two constructors, you need to have only one, but add a third parameter, for example:

Complex (double re, double img, enum type) {
  if(type == polar) {
    ..treat as polar coordinates..
  } else {
    ..treat as cartesian coordinates..
  }
}

'enum type' can have a default type as well so you don't have to supply what type of coordinates you intend to use every time, the default should off course be what's most used.

Complex (double re, double img, enum type = polar);
jimka
It might be better if you *don't* supply a default. Force the user to think about which coordinate set he wants. It'll help make wrong code look wrong.
Kristo
A: 

For me the more elegant way would be to create a class representing cartesian coordinates and another representing polar coordinates, then give object of the relevant class to the constructors.

That way you would have two constructor using different parameters.

Yanik
+1  A: 

I would probably create more classes, but then I am an adept of Boost Strong Typedef library.

For me it does not make sense to use double to represent both coordinates, module and angles.

So I would have:

class CoordinateX {};
class CoordinateY {};

class Modulus {};
class Angle {};

class Complex
{
public:
  Complex(CoordinateX, CoordinateY);
  Complex(Modulus, Angle);
};

And there it's perfectly clear, and there is no ambiguity. Also you add some compile-time check of "units" in a loose sense. It rarely makes sense (if ever) to add an X-coordinate and an Y-coordinate, or worse a distance and an angle.

Matthieu M.