How to propagate friend for derived classes

六眼飞鱼酱① 提交于 2019-12-03 11:45:13

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!