Reputation: 9660
I have a type that I want to expose several predefined static instances of itself. I also want the constructor used to instantiate these static instances to be private. How can I do that?
class Day
{
public:
// Predefined instances.
static Day Monday;
static Day Tuesday;
// ...
private:
// Data.
tstring _longName;
tstring _shortName;
int _id;
// Implementation ???
};
I have this, which has the interface I'm looking for. But it has my spider senses tingling. Even if there's nothing else wrong with it I suspect it falls foul of the static initialization order fiasco.
Day.h
class Day
{
public:
static Day Monday;
static Day Tuesday;
private:
Day(int id, tstring longName, tstring shortName) : _id(id), _longName(longName), _shortName(shortName) {}
friend Day CreateDay(int id, tstring longName, tstring shortName);
tstring _longName;
tstring _shortName;
int _id;
};
Day.cpp
Day CreateDay(int id, tstring longName, tstring shortName)
{
return Day(id, longName, shortName);
}
Day Day::Monday = CreateDay(0, _T("Monday"), _T("Mon"));
Day Day::Tuesday = CreateDay(1, _T("Tuesday"), _T("Tue"));
Upvotes: 0
Views: 61
Reputation: 9660
There is indeed a large problem in the solution proposed in my question. Friend function declarations default to public linkage, which means the friend declaration in my solution, even though it is declared private, is in fact accessible throughout the entire program (this is one of those occasions when I hate C++ with a passion).
It is possible to override the default public linkage of the friend function declaration by preceeding it by a static declaration but that just creates even more problems. Firstly any other translation unit that references the header containing the static function declaration would fail to compile complaining that this function is not defined in that translation unit. And worse, the referencing translation unit could then define the function itself, and its definition would also have friend access to the class allowing it to do whatever it likes, thus creating a total breach of the classes' encapsulation.
A different solution is required, a friend struct. E.g.
// Foo.h
class Foo
{
public:
static Foo Foo1;
private:
Foo(int a) : _a(a) {}
int _a;
friend struct FooStaticInit;
};
// Foo.cpp
struct FooStaticInit
{
static Foo CreateFoo(int a)
{
return Foo(a);
}
};
Foo Foo::Foo1 = FooStaticInit::CreateFoo(1);
This declares CreateFoo in such a way that it can be used to initalise the static Foo members using a private constructor, but CreateFoo cannot be accessed outside Foo.cpp.
Upvotes: 0
Reputation: 13
The initialization order fiasco is coming from the fact that your constructor have this order in the member initialization zone:
_id(id), _longName(longName), _shortName(shortName)
Whereas the class attributes are in an other order:
tstring _longName;
tstring _shortName;
int _id;
Then you should change one of both order to fix the warnings.
Upvotes: -1