For what it's worth... you might also gain from using another idiom.
I know the Flyweight
pattern is quite all the rage, but here you could also benefit from not allocating those millions of objects.
If it seems strange, think of the String
object in Python
. As in many recent languages, String
are immutable in Python
. Of course, the object you manipulate can change, but the real String
does not: your handle is simply relocated.
Of course, Python
has automatic garbage collection which makes it much easier, yet it could work for you too. Here is a sketch:
class FooImpl;
class Foo
{
public:
explicit Foo(int i): mImpl(FooImpl::Build(i)) {}
int foo() const { return mImpl->foo(); }
void foo(int i) { mImpl = mImpl->foo(i); }
private:
const FooImpl* mImpl;
}; // class Foo
class FooImpl
{
public:
static const FooImpl* Build(int i)
{
typedef std::unordered_set<FooImpl> foos_type;
FooImpl tmp(i);
foos_type::iterator it = gFooImplCollection.insert(tmp);
return &(*it);
}
int foo() const { return mFoo; }
const FooImpl* foo(int i) const
{
return Build(i);
}
// Useful thingy
bool operator==(const FooImpl& rhs) const { return mFoo == rhs.mFoo; }
size_t hash() const { return mFoo; }
private:
explicit FooImpl(int i): mFoo(i) {}
int mFoo;
};
std::unordered_set< FooImpl > gFooImplCollection;
Of course, this is very rough, just to give you an idea. If the potential number of different items is important, you need Garbage Collection.
Garbage Collection being another topic, I prefer to leave you with the idea of an Immutable
core class (exposes only const
methods) and a mutable handle (which simply changes the core class it points to when asked to change).
And now that you've taken the time to read, Boost has it: Boost.Flyweight :)
Note:
It seems important to precise, because Foo
is supposed to be allocated (on the stack) millions of times, its size should remain as close as possible to a pointer. This is accomplished by using Intrusive
reference couting (I hope that's what Boost does). Also, it is unnecessary to have virtual
methods in Foo
, the virtual
are in FooImpl
and the Build
may in fact call an AbstractFactory
behind the scenes.
Thus, since Foo
:
- does not have any base class
- does not have any virtual methods
- only has one attribute (a pointer)
Its effective size will be the size of the pointer... which is the best you can hope for if you don't want to store an id an incur lookup cost at each call :)