问题
I want to have a class hierarchy and be able to create objects from it only inside a Factory.
Example:
class Base
{
protected:
Base(){};
virtual void Init(){};
friend class Factory;
};
class SomeClass : public Base
{
public://I want protected here! Now it's possible to call new SomeClass from anywhere!
SomeClass(){};
void Init(){};
};
class Factory
{
public:
template<class T>
T* Get()
{
T* obj = new T();
obj->Init();
return obj;
}
};
int main()
{
Factory factory;
SomeClass *obj = factory.Get<SomeClass>();
}
My problem is that I want to be able to make objects only from Factory, but I don't want to declare friend class Factory
in every class derived from Base.
Is there any way to propagate friend in derived classes? Is there any other way to achieve this behavior?
回答1:
No, it's deliberately impossibile.
Is an issue by encapsulation.
Suppose to have a class "PswClass" that manage any password, that is cascade friend with other class: if I inherit from PswClass:
class Myclass : public PswClass {
.......
}
In this way I can, maybe, have access to field that it would be private.
回答2:
Friendship is neither inherited nor transitive, as described here: friend class with inheritance.
After a little experimentation, and making some use of this hack How to setup a global container (C++03)?, I think I have found a way give the "factory" unique rights to create the objects.
Here's a quick and dirty code. (Scroll towards the bottom to see the hack.)
class Object {};
class Factory {
public:
// factory is a singleton
// make the constructor, copy constructor and assignment operator private.
static Factory* Instance() {
static Factory instance;
return &instance;
}
public: typedef Object* (*CreateObjectCallback)();
private: typedef std::map<int, CreateObjectCallback> CallbackMap;
public:
// Derived classes should use this to register their "create" methods.
// returns false if registration fails
bool RegisterObject(int Id, CreateObjectCallback CreateFn) {
return callbacks_.insert(CallbackMap::value_type(Id, createFn)).second;
}
// as name suggests, creates object of the given Id type
Object* CreateObject(int Id) {
CallbackMap::const_iterator i = callbacks_.find(Id);
if (i == callbacks_.end()) {
throw std::exception();
}
// Invoke the creation function
return (i->second)();
}
private: CallbackMap callbacks_;
};
class Foo : public Object {
private: Foo() { cout << "foo" << endl; }
private: static Object* CreateFoo() { return new Foo(); }
public:
static void RegisterFoo() {
Factory::Instance()->RegisterObject(0, Foo::CreateFoo);
}
};
class Bar : public Object {
private: Bar() { cout << "bar" << endl; }
private: static Object* CreateBar() { return new Bar(); }
public:
static void RegisterBar() {
Factory::Instance()->RegisterObject(1, Bar::CreateBar);
}
};
// use the comma operator hack to register the create methods
int foodummy = (Foo::RegisterFoo(), 0);
int bardummy = (Bar::RegisterBar(), 0);
int main() {
Factory::Instance()->CreateObject(0); // create foo object
Factory::Instance()->CreateObject(1); // create bar object
}
回答3:
No, there is no way to inherit friend
declaration from base class. However, if you make Base
constructor private
, instances of derived classes won't be possible to create without Factory
help.
回答4:
As others already said, friendship is not inheritable.
this looks like a good candidate of "Abstract Factory" pattern.
assume "SomeClass"es derived from base are used polymorphically. declare a abstract factory base, which creates Base objects. derive each concrete factory from base, override the base creation method...
see http://en.wikipedia.org/wiki/Abstract_factory_pattern for examples
回答5:
You can't do that. This is done to protect encapsulation. See this post: Why does C++ not allow inherited friendship?
回答6:
For future reference, another idea that came out of the chat between OP and me, which works with only one use of friend
as the OP wanted. Of course, this is not a universal solution, but it may be useful in some cases.
Below code is a minimal one which shows the essential ideas. This needs to be "integrated" into the rest of the Factory
code.
class Factory;
class Top { // dummy class accessible only to Factory
private:
Top() {}
friend class Factory;
};
class Base {
public:
// force all derived classes to accept a Top* during construction
Base(Top* top) {}
};
class One : public Base {
public:
One(Top* top) : Base(top) {}
};
class Factory {
Factory() {
Top top; // only Factory can create a Top object
One one(&top); // the same pointer could be reused for other objects
}
};
回答7:
It is not possible. As others have said friendship is not inherited.
An alternative is to make all class hierarchy constructors protected and add the factory function/class as friend to all the classes you're interested in.
来源:https://stackoverflow.com/questions/13916321/how-to-propagate-friend-for-derived-classes