Reputation: 11
I need to grab an object by reference and I used to do it like so:
MyObject& obj = FactoryThatGivesAnObject();
obj.MethodThatModifieObj();
No I need to do it based on a conditional:
MyObject obj;
// Need obj to be a reference to the returned values below
if( foo )
obj = FactoryThatGivesAnObject();
else
obj = OtherFactoryThatGivesAnObject();
obj.MethodThatModifiesObj();
How can I have obj be a reference in the second example?
Upvotes: 1
Views: 203
Reputation: 1207
Here's one solution which is not technically a factory, but solves the same problem -- providing new objects when you change parameters:
struct A
{
int a;
float x;
int c;
};
class ObjectCollection
{
public:
ObjectCollection() { m_a.c = 10; }
A &get_obj_a(int a, float x)
{
m_a.a = a;
m_a.x = x;
return m_a;
}
private:
A m_a;
};
This version has advantage that it does not pass around ownership to the object, but still you can create different kinds of objects with it. Two calls to get_obj_a() will cause problems though, it only works if you call get_obj_a() immediately before you need the object. Now the if statement can be put inside the factory function. Also here's another way to do it:
class DerivedFactory
{
public:
DerivedFactory(ObjectCollection1 &c, ObjectCollection2 &c2) : c(c),c2(c2) { }
Base &get_obj_a_or_b(bool b) {
if (b) return c.get_obj_a(10,11.0);
else return c2.get_obj_b(20.0,13.0);
}
private:
ObjectCollection1 &c;
ObjectCollection2 &c2;
};
Upvotes: 0
Reputation: 9172
References, unlike pointers, can only be set once. This is a useful feature many times, but this is the one frustrating aspect about it. You only want to set the reference once, but possibly to different things.
You have two options.
1) Use the ternary operator
This is often the easiest, if you're only dealing with two factories, and a simple boolean to decide which to use:
MyObject& obj = ( foo
? FactoryThatGivesAnObject();
: OtherFactoryThatGivesAnObject() );
However, if foo
is more complicated, or if you have multiple factory options, the next option may be cleaner.
2) Use a factory method of your own
MyObject& get_an_object(const int state) // or whatever parameters you need
{
switch(state)
{
case USE_LEGACY_FACTORY: return FactoryThatGivesAnObject();
case USE_FOO_FACTORY: return OtherFactoryThatGivesAnObject();
case DO_SOMETHING_ELSE: return YetAnotherObjectFactory();
}
throw std::runtime_error("Bad Factory Selector");
}
// usage is simpler now
MyObject& obj = get_an_object(foo);
Note that you may need to pass several parameters to your factory method:
foo
- a simple boolean. As things grow, you may need additional criteria to help determine which factory to use.Upvotes: 5
Reputation: 476950
Your very first line is shady:
MyObject& obj = FactoryThatGivesAnObject();
How is that supposed to work? The factory method cannot return a reference to a temporary, so the only sensible reference it could return is to a dynamically created object - but now who is responsible for this object?
(Unless you are just returning a reference to an existing object, that is. But I'm assuming that your factory is genuinely creating new objects.)
This code is a memory-leak car crash; I don't see any way to write anything sensible like that. A far better way is to return the newly created object in a responsible container, e.g. a shared_ptr
or a unique_ptr
:
#include <memory>
std::unique_ptr<MyObject> FactoryFunction()
{
return std::unique_ptr<MyObject>(new MyObject(3,5,7));
}
That way, if nobody picks up the factory product, or if an exception occurs, the dynamically allocated object will get properly disposed of.
This also makes it trivial to assign different pointers depending on a conditional:
std::unique_ptr<MyObject> x;
if (...) { x = Factory1(); }
else if (...) { x = Factory2(a,b); }
else { x = Factory3(argc, argv); }
Upvotes: 2
Reputation: 106530
How can I have obj be a reference in the second example?
You can't. References are aliases; you can only create them by pointing them at something, and once you've pointed them, they cannot be reassigned.
You would probably be better off using something like a std::auto_ptr
or std::unique_ptr
here. Note that your factory would need to return the auto/unique_ptr though. If your factory is returning a reference I suspect you might be accidentially returning references to unnamed temporaries (unedefined behavior), but without seeing the factories' code it's difficult to tell.
Upvotes: 1
Reputation: 10880
One solution may be to use a ternary operator:
obj = foo ? FactoryThatGivesAnObject() : OtherFactoryThatGivesAnObject();
You could also use a pointer:
MyObject* pobj;
if( foo )
pobj = &FactoryThatGivesAnObject();
else
pobj = &OtherFactoryThatGivesAnObject();
Upvotes: 3