开发者

How to differentiate two constructors with the same parameters?

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 t开发者_运维技巧he same: what is the more elegant way to identify what is intended? Adding a third parameter to one of the constructors?


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);


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.


Use the named constructor idiom described here at the Parashift C++ FAQ.


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...


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);


Since no one mentioned it, you can use tags:

class Complex{
    struct CartCoord{};
    struct PolarCoord{};

    Complex( CartCoord, double re, double img);
    Complex( PolarCoord, double a, double v);
};

int main(){
    auto c1 = Complex(Complex::CartCoord{},  5, 6);
    auto c2 = Complex(Complex::PolarCoord{}, 5, 6);
}


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.


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.


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.


I use this simple alternative way while practicing C++, I don't know if it is what you want.

class Vector{
    public:
    float x, y;

    Vector();
    Vector(float , float, bool);

    float get_distance(Vector v); // find distance from another vector
    float get_magnitude();        // find distance from origin point
    Vector add(Vector v);
    Vector subtract(Vector v);
    Vector multiply(Vector v);      // multiply by vector
    Vector multiply(float scalar);  // multiply by scalar
    float get_angle();
};

Vector::Vector(float a, float b, bool is_cartesian = true){
    if(is_cartesian){
        x = a;
        y = b;
    }
    else{
        x = a * cos( b );
        y = a * sin( b );
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜