3

I am in the process of making a simulation and it requires the creation of multiple, rather similar models. My idea is to have a class called Model and use static factory methods to construct a model. For example; Model::createTriangle or Model::createFromFile. I took this idea from previous java code and was looking for ways to implement this in C++.

Here is what I came up with so far:

#include <iostream>

class Object {
    int id;

public:
    void print() { std::cout << id << std::endl; }

    static Object &createWithID(int id) {
        Object *obj = new Object();

        obj->id = id;

        return *obj; 
    }
};

int main() {
    Object obj = Object::createWithID(3);

    obj.print();

    return 0;
}

Some questions about this:

  • Is this an accepted and clean way of making objects?
  • Does the returned reference always ensure correct removal of the object?
  • Is there any way to do this without pointers?
Wilco
  • 928
  • 3
  • 9
  • 20

6 Answers6

9

Just for the record, here's how this program might look like in proper C++:

class Object
{
    int id;

    // private constructor, not for general use
    explicit Object(int i) : id(i) { }

public:
    static Object createWithID(int id)
    {
        return Object(id);
    }
};

int main()
{
    Object obj1 = Object::createWithID(1);
    auto obj2 = Object::createWithID(2);   // DRY

    // return 0 is implied
}

This is probably not what people would generally call a "factory", since factories typically involve some dynamic type selection. The term "named constructor" is sometimes used, though, to refer to the static member function that returns an instance of the class.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • +1: Perfectly answers the question and provides useful additional information. – Mankarse May 24 '14 at 12:33
  • 1
    The question's code is just as well "proper C++". Neither code example is proper design (in the answer's code having a private constructor that's exposed by factory functions is just added complexity, verbosity and possibly inefficiency). Unfortunately it's not clear what the OP's code is *for*. – Cheers and hth. - Alf May 24 '14 at 12:37
  • @Cheersandhth.-Alf: Point taken... though there's only so much that can be done without OP supplying more information. – Mankarse May 24 '14 at 12:41
8

Your code currently contains a memory leak: any object created using new, must be cleaned up using delete. The createWithID method should preferably not use new at all and look something like this:

static Object createWithID(int id) 
{
    Object obj;
    obj.id = id;
    return obj; 
}

This appears to require an additional copy of the object, but in reality return value optimization will typically cause this copy to be optimized away.

TC.
  • 4,133
  • 3
  • 31
  • 33
  • There is no reason to use a factory function where a constructor will do. Constructors are designed to construct. Why not use them. – Cheers and hth. - Alf May 24 '14 at 12:47
  • 5
    I don't agree on this, constructors have a limitation: they do not scale well to large numbers of optional parameters. Also, constructor methods do not imply what is being created, while a function named **createFromFile** tells you exactly what happens. – Wilco May 24 '14 at 12:50
  • @Wilco:re "Also, constructor methods do not imply what is being created", with a constructor the type is mentioned right there, so this sentence is sheer lunacy. But thanks for making these points. – Cheers and hth. - Alf May 24 '14 at 12:54
  • 11
    @Cheersandhth.-Alf I think the point Wilco was trying to make was that the name of a constructor doesn't imply **how** a class will be created not **what** class will be created. And instead of multiple constructors or a constructor with multiple optional parameters you can instead have multiple factory methods with meaningful names and fewer optional parameters or meaningless bool parameters. There are pros and cons of factory methods but they are clearly not "absurd" or "lunacy". – Chris Drew May 24 '14 at 21:57
5

Is this an accepted and clean way of making objects?

It is (unfortunately) accepted but it's not clean.

Instead of factory functions just use constructors.

That's what they're for.

Does the returned reference always ensure correct removal of the object?

The reference is irrelevant except to the extent that it misleads users of the function.

In your example the reference has apparently misled yourself into not destroying the dynamically allocated object, but just copying it.

Better return a smart pointer.

But as already mentioned, it's even better to ditch the idea of factory functions.

They're wholly unnecessary here.

Is there any way to do this without pointers?

No, not if "this" refers to the dynamic allocation, but you can and should use constructors instead of factory functions.


Example:

#include <iostream>

namespace better {
    using std::ostream;

    class Object
    {
    public:
        auto id() const -> int { return id_; }
        explicit Object( int const id): id_( id ) {}
    private:
        int id_;
    };

    auto operator<<( ostream& stream, Object const& o )
        -> ostream&
    { return (stream << o.id()); }
}  // namespace better

auto main()
    -> int
{
    using namespace std;
    cout << better::Object( 3 ) << endl;
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • A factory function would be unnecessary if this was the only one but OP said they need many of these functions that make the class differently. Factory functions are one way of solving that. – Chris Drew May 24 '14 at 22:53
  • @ChrisDrew: Yes factory functions can be appropriate, in a few cases. A main example is `std::make_shared`. Although changing the design can be a better solution (it's a pity that `boost::intrusive_ptr` didn't make it into C++11). Just doing the job of constructors is however not a case where factory functions are a good choice. As simple constructor replacements they have negative utility, adding complexity, verbosity, fragility, needless restrictions (on both instantiation and class derivation), ad hoc naming and possibly also inefficiency, with no advantage. Technically an ungood choice. – Cheers and hth. - Alf May 24 '14 at 23:14
  • However, the C++ FAQ does recommend factory functions in some cases. Mainly because that was the consensus in comp.lang.c++ before people, including me, pointed out that making a single destructor protected is cleaner and much less work, O(1) versus O(n), than making constructors protected. I'm not sure why it wasn't updated about this, I think I did discuss it with Marshall. – Cheers and hth. - Alf May 24 '14 at 23:18
  • Quite possibly, but you need to show how you would use constructors to answer OP's question. How would you do "createTriangle() " and, I assume, "createSquare()" etc – Chris Drew May 24 '14 at 23:22
  • @Chris: re "createTriangle()" factory function, replace that with a class Triangle constructor. i have already stated this, a number of times. it should not be necessary to repeat it for every new factory function name one can think of. – Cheers and hth. - Alf May 24 '14 at 23:25
  • Maybe I misunderstood the question. But I was assuming createTriangle still returned the same "Model" class, just a model of a triangle. – Chris Drew May 24 '14 at 23:30
  • Assume that it does, in the OP's code. The introducing a Triangle derived class is a good idea. Among other advantages, it does not immediately throw away the knowledge that the result represents a triangle. – Cheers and hth. - Alf May 24 '14 at 23:35
  • Not always a good idea. Especially in the context of a simulation model. It would be like saying it is a good idea to have a Face class that derives from Picture for all pictures of faces. – Chris Drew May 24 '14 at 23:40
  • @Chris: You were supposing that Model could *dynamically* represent a triangle. Now you're saying that it's probably not a good idea to capture that knowledge *statically*. That does not make sense. – Cheers and hth. - Alf May 24 '14 at 23:42
2

By calling Object *obj = new Object(); you do allocate memory on the heap. In the lines following that statement you do return the reference to that object. So far, so good, but you do never delete the object you created to actually free the memory. By calling the function several times you will run in a memory leak.

There are two possible workarounds:

  1. static Object createWithID(int id); would return a copy of the Object you create, so it would be enough to allocate it on the stack using

    Object tmp;
    tmp.id = id;
    
  2. use c++11 smart pointer to let them handle the memory.

    #include <memory>
    static std::unique_ptr<Object> createWithID(int id)
    {
        std::unique_ptr<Object> tmp(new Object());
        tmp->id = id;
        return std::move(tmp);
    }
    
Theolodis
  • 4,977
  • 3
  • 34
  • 53
1

This is an absolutely terrible way to create your objects. Every time that createWithID is called, a new Object is constructed on the free store which is never able to be destroyed.

You should rewrite createWithID to:

static Object createWithID(int id) {
    Object obj;
    obj.id = id;
    return obj; 
}

Or better, you could just supply a constructor for your Object objects.

If you want to enable polymorphic objects, you should use something like wheels::value_ptr.

Mankarse
  • 39,818
  • 11
  • 97
  • 141
1

Unless you are using polymorphism there is no reason for your factory functions to return any kind of pointer, they can just return the object by value. Any modern compiler will do return value optimization so there is no copy.

If you are after an "accepted and clean" way then that sounds quite opinion based and dependent on how this class will be used but what I would do is keep the definition of Model as small as possible. Only include what is needed for it to do its job with a minimum number of constructors required for normal usage:

namespace Simulation {
  class Model {
   private: 
    int id_;
   public:
    explicit Model(int id) : id_(id) {}

    // minimum required to do the job...
  };
}

Then, I would define the functions to create various flavors of Model separately. For example, as non-member, non-friend functions in a namespace:

namespace Simulation {  
  Model createTriangle(int id) {
    Model model(id);
    // do whatever you need to do to make it a triangle...
    return model;
  }

  Model createSquare(int id) {
    Model model(id);
    // do whatever you need to do to make it a square...
    return model;
  }  
}

That way, if you find you need another flavor of Model, you don't need to change the Model class. Your create functions can even be spread across multiple files if needed or become part of a Builder or Factory class. Usage would look like:

int main() {
  Simulation::Model m1(0);
  Simulation::Model m2 = Simulation::createTriangle(1);
  Simulation::Model m3 = Simulation::createSquare(2);
}
Chris Drew
  • 14,926
  • 3
  • 34
  • 54