【问题标题】:When/why to make function private in class?何时/为什么在课堂上将函数设为私有?
【发布时间】:2011-05-29 04:51:32
【问题描述】:

我什么时候应该创建一个函数private,为什么这是个好主意?

【问题讨论】:

  • 我会投票支持提供具体示例的答案。目前,答案还不够具体,无法反驳。

标签: c++ oop class-design private-methods


【解决方案1】:

我将在这里提出一个反论点:而不是创建函数 private,它通常应该是 protectedvirtual。 以API的调用或使用没问题(也就是public函数没问题),但类的实现不够,或者需要扩展的情况。 您(当然)会派生基类,并为需要更改的公开的public 函数提供新定义。

第一个问题是派生类是否没有足够的权限访问内部变量或辅助函数来实现改进。即,要重写公开的 api 函数,没有辅助函数是不可能的。借用Nawaz's excellent answer above,因为我会将一些外部定义的类SomeClass 作为参数传递给public-ly 公开的apivoid DoWork(SomeClass param),如果需要重写,则使用辅助函数bool IsValidParam(SomeClass param) 对其进行验证DoWork,如果没有IsValidParam这样的现有辅助函数会很痛苦,只能用protected来完成。

第二个问题是您是否需要覆盖内部函数,而不是重新实现整个面向公众的调用函数。在这种情况下,您可以声明您的内部辅助函数virtualSee also When to use Private Virtual。像上面的例子一样,如果我更改了SomeClass,或者一个派生类,或者在不同的上下文中需要不同的(更严格?更宽松?)验证,我可能只覆盖virtual IsValidParam,而单独保留DoWork 函数。一般来说,如果您可能继承一个类,或将其用作基类,it's better to make it's functions virtual anyway

根据软件的性质或架构的设计,在早期阶段声明所有函数 public virtual 或/然后 protected virtual 可能会很有用,因为您限制了公开的 api。最后,根据需要拉出virtual或声明private。但是虽然不受限制,但它使下游改进更容易;特别是如果它减少了编辑已经“固定”或难以更改的库的需要。

【讨论】:

    【解决方案2】:

    你想成为多么纯粹的人? :)

    这个问题的正确答案与不变量的维护有关。正确的做法是相当复杂的。

    在您的基类中定义公共方法以提供对类的整体访问。所有这些方法都必须是具体的。这里的关键是假定公共不变量在调用这些函数之前和之后都成立。这些函数绝不能相互调用,它们只调用受保护的和私有的方法。这些函数应该是公理的:它们应该是捕获所需语义所需的相当小的集合。

    可以使用这些方法完成的大多数计算应该是全局或至少是公共静态成员。

    您还提供纯虚方法,这些方法是根据派生类中的表示来实现细节的钩子。基础中的虚拟功能应该是私有的。这里(公开)的通常建议是完全错误的。虚函数是实现细节。一个例外:虚拟析构函数必须是公共的。

    私有辅助函数也可以放在基层中。

    在基类中包含受保护的方法也可能很有用:这些将调用私有助手或虚拟函数。如上所述:受保护的方法不应该调用受保护的或公共的方法。受保护的函数在每次调用之前和之后都保持比公共函数更弱的不变量。

    私有函数通常保持非常弱的不变量。

    这种严格层次结构的原因是为了确保正确维护对象不变量。

    现在,在派生类中,您提供了一个实现。理想情况下,这里的虚函数应该是不可见,这是一个比私有的更强大的条件。这些虚函数根本不应该在派生类中调用。 允许通过基础的受保护功能进行访问。

    派生类的所有方法,包括析构函数都应该是私有的。构造函数必须是公共的,但它们不是类的方法。

    为了完全理解这些规则,您必须仔细考虑不变量。公共不变量可以在调用公共方法之前假定为保持,并且在完成后要求保持。因此,您不能从类内部或从它派生的任何类中调用此类函数,因为这些函数用于修改公共函数开始和结束之间的表示,因此不可避免地会破坏公共不变量:这就是为什么它们必须不调用公共函数。

    同样的论点适用于受保护的函数:函数只能调用具有较弱不变量的函数

    虚拟函数总是由基类公共包装器中的公共调用,以确保破坏公共不变量的操作顺序永远不会因返回带有损坏不变量的公共而中断。虚集本身代表了任何表示必须具有的不变结构。通过这样做,所有为公共客户端执行计算的表示操作都可以抽象到基础中。

    在实践中,通常不遵循这些规则,因为它们通常会生成微不足道的包装器,并且需要编写和维护大量额外代码。所以虚函数通常最终会公开,即使这在原则上是完全错误的。

    【讨论】:

    • “因此你不能从类内部调用这样的函数”——这不符合。 如果不变量在调用时不成立,则不能调用它们,但您可以在碰巧知道类不变量确实成立的地方调用它们,例如如果调用代码尚未修改任何数据成员的情况。例如,std::string 的函数 sizelength 返回完全相同的值。没有什么特别的原因不能实现一个调用另一个并返回结果。 size 不会“不可避免地破坏公共不变量”。
    • 对于一个非常量的例子,考虑std::vector::pop_back,它可以通过调用另外两个公共成员来相当合理地实现:erase(--end())。您具体谈论的是 NVI 习语的实现,其中每个公共函数除了调用私有虚函数外什么都不做。但这不是在 C++ 中编写类的唯一好方法,将虚函数设为私有的原因是不是,以便在对象处于违反其类的状态时调用它们不变量。他们通常不能。
    【解决方案3】:

    private:只被这个类使用,不被其他类和派生类使用。
    protected:被这个类和派生类使用,但不被其他类使用。
    public:由其他类、此类和派生类使用。

    很难在私有和受保护之间做出选择。因此,如果派生类有 1% 的机会需要它,我总是将函数设为 protected。

    【讨论】:

      【解决方案4】:

      我通常制作辅助函数private。但是 helper 是什么似乎很模糊。所以让我给你举个例子。假设您有以下课程Sample;它公开了一些公共功能,其中之一是DoWork()。该函数采用一个参数。但是它并不假设参数总是有效的,所以它首先检查参数的有效性,它在函数的开头有很多代码。像这样的:

      class Sample
      {
         public:
            void DoWork(SomeClass param)
            {
                     /*
                      *lots of code related to validation of param
                      */  
      
                      //actual code that operates on the param 
                      //and other member data IF the param is valid
            }
      };
      

      由于您编写了很多与参数验证相关的代码,这使得函数变得繁琐且难以阅读。因此,您决定将此验证代码移至函数IsValidParam(),然后从DoWork() 调用此函数,并将参数param 传递给它。像这样的:

      class Sample
      {
         public:
            void DoWork(SomeClass param)
            {       
                  if ( IsValidParam(param))       
                  {
                      //actual code that operates on the param 
                      //and other member data IF the param is valid
                  }
            }
      };
      

      这样看起来更干净,对吧?

      好的,你在课堂上的某个地方写了IsValidParam(),但你现在面临的问题是,你会做这个函数public吗?如果此函数由您的其他函数(例如 DoWork())使用,那么将 IsValidParam()public 设为没有意义。所以你决定做这个函数private

      class Sample
      {
         public:
            void DoWork(SomeClass param)
            {       
                  if ( IsValidParam(param))       
                  {
                      //actual code that operates on the param 
                      //and other member data IF the param is valid
                  }
            }
        private:
            bool IsValidParam(SomeClass param)
            {
                //check the validity of param.
                //return true if valid, else false.
            }
      };
      

      此类函数 (IsValidParam) 应为 private。我将这些函数称为辅助函数

      希望这个解释对你有帮助!

      【讨论】:

        【解决方案5】:

        在创建函数或类之前,我们应该了解该函数或类的范围,无论它是全局的还是本地的。

        例如:“ConnectionString ()”。 每个数据库连接都需要“ConnectionString ()”,所以它声明为 Public。

        【讨论】:

          【解决方案6】:

          当您不需要其他对象或类来访问该函数时,您应该创建一个函数private,此时您将从类中调用它。

          坚持最小权限原则,只允许访问绝对必要的变量/函数。不符合此标准的任何内容都应为private

          【讨论】:

          • (仅供参考,因为您已经强调了在更广泛的 OO 圈子中首选的“方法”术语)“函数”是 C++ 标准中使用的术语,在 C++ 上下文中非常正确。
          【解决方案7】:

          如果您正在设计一个类 - 考虑到客户端代码应该使用它的方式 - 那么您将不可避免地派生一个由 public 和可能 protected 成员组成的接口。

          private 成员是支持和启用这些公共/受保护成员的函数和数据。 private 函数应该考虑和/或模块化/结构化非private 成员所需的代码,从而减少它们的实现冗余和更容易理解。

          总之,如果private 不是供客户端代码直接使用的成员,并且仅用于支持非private 成员,则可以创建该成员。

          【讨论】:

            【解决方案8】:

            OOP 的创始原则之一是封装。这是对象如何工作的功能保留在该对象内部的地方。这个想法是,如果代码不需要知道它是如何工作的,那么它更容易使用一个对象。有点像买微波炉——你只需要知道如何使用它,而不是它是如何工作的。

            OOP 也应采用相同的方法。保持保持对象私有所需的一切。仅公开充分使用对象所需的内容。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-10-12
              • 1970-01-01
              相关资源
              最近更新 更多