【问题标题】:How to propagate friend for derived classes如何为派生类传播朋友
【发布时间】:2012-12-04 16:14:05
【问题描述】:

我希望有一个类层次结构,并且只能在工厂内部从它创建对象。

例子:

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>();
}

我的问题是我希望能够仅从 Factory 生成对象,但我不想在从 Base 派生的每个类中声明 friend class Factory

有没有办法在派生类中传播朋友?有没有其他方法可以实现这种行为?

【问题讨论】:

  • 你不能继承friendship。看到这个答案:stackoverflow.com/questions/13844129/…
  • 请注意,protected 的限制仅比public 稍微严格一些。任何人都可以从SomeClass 继承并提供进入其protected 成员的途径。

标签: c++ friend


【解决方案1】:

不,这是故意不可能的。

是封装的问题。

假设有一个管理任何密码的类“PswClass”,它与其他类是级联朋友:如果我从 PswClass 继承:

 class Myclass : public PswClass {
     .......
 }

通过这种方式,我也许可以访问私有的字段。

【讨论】:

    【解决方案2】:

    友谊既不是继承的也不是传递的,如下所述:friend class with inheritance

    经过一些实验,并利用这个 hack How to setup a global container (C++03)?,我想我找到了一种方法,赋予“工厂”创建对象的独特权利。

    这是一个快速而肮脏的代码。 (向下滚动以查看 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
    }
    

    【讨论】:

    • 我读过这本书,确实是一本很棒的书:) 我想让所有派生类都易于编写,并限制对象的创建。使用此方法,任何人都可以不受任何限制地调用 CreateX,并且每次添加新类型时都必须更改“全部注册”功能。我更喜欢静态注册:)
    • 啊,对不起,我想我忽略了关于没有其他人应该创建它的部分。让我再考虑一下。
    • @Felics 等一下,我发现了一个看起来很丑(但“有效”)的解决方法,它还满足了您禁止从其他地方构造对象的要求。让我稍后发布代码。
    • @Felics 好吧,再想想,也许不是。您仍然需要为每个类创建一个虚拟对象,但它可以满足您的要求,而且我认为这比 friending Factory 更安全。让我知道这是否适合您。
    • 你可以创建 int RegisterX() 并且你不需要逗号 hack:) 你也可以创建一个在构造函数中注册类型的类,然后声明一个“注册类型”的变量。它更干净:)
    【解决方案3】:

    不,没有办法从基类继承friend 声明。但是,如果您将Base 构造函数设为private,则在没有Factory 帮助的情况下将无法创建派生类的实例。

    【讨论】:

      【解决方案4】:

      正如其他人已经说过的,友谊是不可继承的。

      这看起来很适合“抽象工厂”模式。

      假设从 base 派生的 "SomeClass" 是多态使用的。 声明一个抽象工厂基础,它创建 Base 对象。 从base派生每个具体工厂,覆盖base创建方法...

      有关示例,请参阅 http://en.wikipedia.org/wiki/Abstract_factory_pattern

      【讨论】:

        【解决方案5】:

        你不能那样做。这样做是为了保护封装。看到这个帖子:Why does C++ not allow inherited friendship?

        【讨论】:

          【解决方案6】:

          为了将来参考,OP 和我之间的 chat 产生了另一个想法,它只适用于 OP 想要的 friend 的一次使用。当然,这不是一个通用的解决方案,但在某些情况下可能会有用。

          下面的代码是显示基本思想的最小代码。这需要“集成”到Factory 代码的其余部分中。

          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】:

            这是不可能的。正如其他人所说,友谊不是遗传的。

            另一种方法是保护所有类层次结构构造函数,并将工厂函数/类作为朋友添加到您感兴趣的所有类中。

            【讨论】:

              猜你喜欢
              • 2011-05-04
              • 2020-08-10
              • 2017-11-22
              • 2018-01-05
              • 2012-12-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-18
              相关资源
              最近更新 更多