Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Named constructor idiom and new operator

Tags:

c++

I use the named constructor idiom to create objects, because I have lots of calls with identical parameters but the object shall be created differently.

The C++ FAQ tell us how to do this. It also tells us how to force objects being heap allocated. Yet it really fails to tell us how to use the named constructor idiom with the new operator.

Because new requires a constructor to be called we cannot directly call named constructors. So I found two workarounds to this problem:

I create an additional copy constructor and hope that optimizing compilers won't create a temporary object.

class point_t {
    int X,Y;
    point_t(int x, int y) : X(x), Y(y) { }
  public:
    point_t(const point_t &x) : X(x.X), Y(x.Y) { }
    static point_t carthesian(int x, int y) { return point_t(x,y); }
    static point_t polar(float radius, float angle) {
      return point_t(radius*std::cos(angle), radius*std::sin(angle));
    }

    void add(int x, int y) { X += x; Y += y; }
};



int main(int argc, char **argv) {
  /* XXX: hope that compiler doesn't create a temporary */
  point_t *x = new point_t(point_t::carthesian(1,2));
  x->add(1,2);
}

The other version is to create separate named constructors. Because function overloading doesn't work on return type I use two different names, which is ugly.

class point_t {
    int X,Y;
    point_t(int x, int y) : X(x), Y(y) { }
  public:
    /* XXX: function overloading doesn't work on return types */
    static point_t carthesian(int x, int y) { return point_t(x,y); }
    static point_t *carthesian_heap(int x, int y) { return new point_t(x,y); }
    void add(int x, int y) { X += x; Y += y; }
};

int main(int argc, char **argv) {
  point_t *x = point_t::carthesian_heap(1,2);
  x->add(1,2);
}

Is there a prettier version that is equal to the example code?

like image 525
Alexander Oh Avatar asked Aug 10 '11 12:08

Alexander Oh


People also ask

What is the named constructor idiom?

[10.8] What is the "Named Constructor Idiom"? A technique that provides more intuitive and/or safer construction operations for users of your class. The problem is that constructors always have the same name as the class.

Is a constructor called using the new operator?

When new is used to allocate memory for a C++ class object, the object's constructor is called after the memory is allocated. Use the delete operator to deallocate the memory allocated by the new operator.

What is name constructor?

The named constructor idiom uses a set of static member functions with meaningful names to create objects instead of constructors. Constructors are either private or protected and clients have access only to the public static functions.

What is new operator?

The new operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.


2 Answers

You can avoid named constructor idiom for this completely, and do it using an additonal dummy enum parameter to select the constructor.

enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
    int X,Y;
  public:
    point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
    point_t(Carthesian, int x, int y) :X(x),Y(y){}
    point_t(Polar, float radius, float angle)
    : X (radius*std::cos(angle)), Y(radius*std::sin(angle)) {}
    void add(int x, int y) { X += x; Y += y; }
};

int main(int argc, char **argv) {
  point_t *x = new point_t(carthesian,1,2);
  point_t *y = new point_t(polar,0,3);
  x->add(1,2);
}

It is simple, portable, and the only overhead you will see is for the passing of the dummy enum values. In the rare case this overhead is too high for you it can be eliminated by wrapping a function call even when the construction itself is not inlined, as follows:

enum Carthesian {carthesian};
enum Polar {polar};
class point_t {
    int X,Y;
    void initCarthesian(int x, int y); // may be long, not inlined
    void initPolar(float radius, float angle);
  public:
    point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
    point_t(Carthesian, int x, int y)
    {initCarthesian(x,y);} // this is short and inlined
    point_t(Polar, float radius, float angle) {initPolar(radius, angle);}
    void add(int x, int y) { X += x; Y += y; }
};

Another approach is to use a derived class for construction. When using inner classes, it leads into quite a nice syntax I think:

class point_t {
    int X,Y;
  public:
    struct carthesian;
    struct polar;
    point_t(int x, int y) : X(x), Y(y) { } // may keep as a default
    void add(int x, int y) { X += x; Y += y; }
};

struct point_t::carthesian: public point_t
{
  carthesian(int x, int y):point_t(x,y){}
};

struct point_t::polar: public point_t
{
  polar(float radius, float angle):point_t(radius*std::cos(angle),radius*std::sin(angle)){}
};

int main(int argc, char **argv) {
  point_t *x = new point_t::carthesian(1,2);
  point_t *y = new point_t::polar(0,3);
  x->add(1,2);
  return 0;
}
like image 73
Suma Avatar answered Oct 03 '22 21:10

Suma


You could write :

point_t *x = new point_t(point_t::carthesian(1,2));

It first calls carthesian() and then the copy-constructor.

Or, is there any problem in it? Perhaps, a bit slow?

By the way, there is one clear advantage in this code: the programmer can clearly see the new operator in his code (where he is using point_t written by someone else), so you can assume that its his responsibility to call delete once he is done with x.

like image 20
Nawaz Avatar answered Oct 03 '22 21:10

Nawaz