【问题标题】:Access modifiers of a variable: Private vs Protected变量的访问修饰符:私有与受保护
【发布时间】:2011-05-14 10:48:50
【问题描述】:

我有一个案例,我找不到合适的解决方案。我需要有模板基类和一个对该基类做一些工作的管理器类。这是代码

class Base;

class Manager
{
public:
    void DoSomethingTo(Base * bclass)
    {
        if(bclass->PrivateVar == 0)
            // Some code
    }
}

class Base
{
protected: 
    int PrivateVar;
friend class Manager;
};

template<class T>
class TempBase : public Base
{
private:
    Manager * man;
public:
    void DoWork()
    {
        PrivateVar = 0;
        man->DoSomethingTo(this);
    }
}

我的问题是 PrivateVar 变量实际上必须是 TempBase 私有的,并且不能从派生自 TempBase 的类中访问,但是由于我无法将其指针传递给 Manager::DoSomethingTo(),因此我必须从基类派生它并且基类必须拥有PrivateVar。而且由于我在 TempBase 中使用 PrivateVar,它不能对 Base 私有,这使得它可以从 TempBase 的子类访问。

如何编写 TempBase 类,使 PrivateVar 成为私有成员,而可以从 Manager::DoSomethingTo() 访问它?

谢谢

【问题讨论】:

    标签: c++ templates access-modifiers


    【解决方案1】:

    首先,让我们开始火焰战争:

    类的属性永远不会是protected。它应该是private(在大多数情况下)或者在某些极端情况下可能是public。请注意,因为每个派生类都会看到一个protected 属性,protected 只会带来一种错误的安全感:无法控制protected 属性上的任何不变量,所以它本质上是一个public,带有注释不要在上面触摸。

    既然说到做到,我建议简单地将其更改为:

    class Base {};
    template <typename T> class TBase;
    
    class DoKey { template <typename T> friend class TBase; DoKey(); };
    
    class Manager
    {
    public:
      void DoSomethingTo(Base& base, DoKey const& key);
    };
    
    template <typename T>
    class TBase: public Base
    {
    public:
      void Do()
      {
        Manager manager;
        if (PrivateVar == 0) { manager.DoSomething(*this, DoKey()); }
      }
    
    private:
      int PrivateVar;
    };
    

    通过将测试的责任委托给变量的所有者,我们轻轻地绕开了我们所有的担忧。当然,如果我们需要多个类以类似的方式表现,那么我们可以在Base 中引入一个纯虚拟void ShouldIDoSomething() const,并在Manager 的方法中检查它。

    【讨论】:

    • 我不确定 DoKey 的用途。
    • @Bahadir:它是一个访问控制器。只有DoKey 声明为友元的类可以构建DoKey 实例,因此只有它们可以调用ManagerDoSomethingTo 方法。它是在Manager 中直接使用friend 的替代品,它只授予对1 个方法而不是整个类内部的访问权限,从而减少了依赖关系。
    【解决方案2】:

    移除Base内部的访问保护,然后私有继承。

    class Manager
    {
    public:
        void DoSomethingTo(struct Base * bclass);
    };
    
    struct Base
    {
        int PrivateVar;
    };
    
    void Manager::DoSomethingTo(struct Base * bclass)
    {
        if(bclass->PrivateVar == 0)
            ; // Some code
    }
    
    template<class T>
    class TempBase : private Base
    {
    private:
        Manager * man;
    public:
        void DoWork()
        {
            PrivateVar = 0;
            man->DoSomethingTo(this);
        }
    };
    

    【讨论】:

      【解决方案3】:

      您可以将受保护的成员函数放入 Base 本身:

      class Base
      {
      protected:
         void sendPrivateVarToManager( Manager& manager )
         {
            manager.DoSomethingToPrivateVar( privateVar );
         }
      };
      

      由于 Base 不是模板,因此实际上可以将实现放在 Base 之外。

      您的另一个问题是您希望 privateVar 存在于 Base 中,对 TempBase 可见,但在层次结构中不可见。您可以将它移到 TempBase 中,在这种情况下,您也可以将 sendPrivateVarToManager 放在该类中,尽管您必须实现它,或者也将 privateVar 作为参数传递给该类。您的另一个选择是让 TempBase 成为 Base 的朋友,但这似乎是错误的。我不确定为什么 TempBase 需要访问权限。

      【讨论】:

      • 从设计上看,我不确定为什么Base 需要访问权限。显然PrivateVar 纯粹是一个TempBase 的东西,它被转移到Base 以便Manager 可以访问它。我的印象是在阅读代码时解开一盘意大利面:/
      • @Matthieu M.:关于为什么Base 需要访问权限,您是绝对正确的。对混乱的代码感到抱歉。
      • @Bahadir:不用担心,当一个人遇到技术问题时,它自然会反映在设计中。我可以声称我自己的几个扭曲的设计,有些希望我仍然有幸维护:D
      【解决方案4】:

      您可以将PrivateVar 设为私有,并同时将TempBaseManager 设为Base 的朋友。见here。请注意,此代码不是最干净的。

      【讨论】:

        【解决方案5】:

        我认为我可以将一个公共纯虚函数放入 Base 以检索 PrivateVar,同时将 PrivateVar 保留在 TempBase 中。

        class Manager
        {
        public:
            void DoSomethingTo(Base * bclass)
            {
                if(bclass->GetPrivateVar() == 0)
                    // Some code
            }
        }
        class Base
        {
        private:
        virtual int GetPrivateVar() = 0;  
        friend class Manager;
        };
        
        template<class T>
        class TempBase : public Base
        {
        private:
            Manager * man;
            int PrivateVar;
            int GetPrivateVar() {return PrivateVar;}
        public:
            void DoWork()
            {
                PrivateVar = 0;
                // Some code
                man->DoSomethingTo(this);
                // Some code
            }
        friend class Manager;
        }
        

        【讨论】:

          猜你喜欢
          • 2016-04-03
          • 1970-01-01
          • 2013-03-05
          • 1970-01-01
          • 2019-05-07
          • 2017-05-16
          • 2014-12-29
          相关资源
          最近更新 更多