我什么时候应该做一个函数private
,为什么这是个好主意?
8 回答
private
当您不需要其他对象或类来访问该函数时,您应该创建一个函数,而您将从类中调用它。
坚持最小权限原则,只允许访问绝对必要的变量/函数。任何不符合此标准的都应该是private
.
我通常制作辅助函数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()
公开没有意义。所以你决定做这个功能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
. 我将这些函数称为辅助函数。
希望这个解释对你有帮助!
OOP 的创始原则之一是封装。这是对象如何工作的功能保留在该对象内部的地方。这个想法是,如果代码不需要知道它是如何工作的,那么它更容易使用一个对象。有点像买微波炉——你只需要知道如何使用它而不是它是如何工作的。
OOP 也应采用相同的方法。保持保持对象私有所需的一切。仅公开充分使用对象所需的内容。
如果您正在设计一个类 - 考虑到客户端代码应该使用它的方式 - 那么您将不可避免地派生一个由成员组成的public
接口protected
。
private
成员是支持和启用这些公共/受保护成员的功能和数据。 private
函数应该分解和/或模块化/结构化非private
成员所需的代码,使其实现更少冗余且更易于理解。
总而言之,private
如果它不打算由客户端代码直接使用,并且仅用于支持非成员,则您创建一个private
成员。
你想成为多么纯粹的人?:)
这个问题的正确答案与不变量的维护有关。执行此操作的正确方法相当复杂。
在您的基类中,您定义公共方法以提供对该类的全部访问。所有这些方法都必须是具体的。这里的关键是假定公共不变量在调用这些函数之前和之后都成立。这些函数绝不能相互调用,它们只调用受保护的和私有的方法。这些函数应该是公理的:它们应该是捕获所需语义所需的相当小的集合。
可以使用这些方法完成的大多数计算应该是全局或至少是公共静态成员。
您还提供纯虚拟方法,这些方法是根据派生类中的表示来实现细节的钩子。基础中的虚拟功能应该是私有的。这里(公开)的通常建议是完全错误的。虚函数是实现细节。一个例外:虚拟析构函数必须是公共的。
私有辅助函数也可以放在基础中。
在基础中拥有受保护的方法也可能很有用:这些将调用私有助手或虚拟函数。如上所述:受保护的方法不应该调用受保护的或公共的方法。受保护的函数在每次调用之前和之后都保持比公共函数更弱的不变量。
私有函数通常保持非常弱的不变量。
这种严格层次结构的原因是为了确保正确维护对象不变量。
现在,在派生类中,您提供了一个实现。理想情况下,这里的虚函数是不可见的,这是一个比仅仅私有的更强大的条件。这些虚函数根本不应该在派生类中调用。唯一允许的访问是通过基座的受保护功能。
包括析构函数在内的派生类的所有方法都应该是私有的。构造函数必须是公共的,但它们不是类的方法。
为了完全理解这些规则,您必须仔细考虑不变量。可以假设公共不变量在调用公共方法之前保持不变,并且在它完成后被要求保持。因此,您不能从类内部或从它派生的任何类中调用此类函数,因为这些函数用于修改公共函数开始和结束之间的表示,因此不可避免地会破坏公共不变量:这就是为什么它们必须不调用公共函数。
同样的论点也适用于受保护的函数:函数只能调用具有较弱不变量的函数。
公众总是从基类公共包装器中调用虚函数,以确保破坏公共不变量的操作的顺序永远不会因返回带有破坏的不变量的公众而中断。虚集本身代表了任何表示必须具有的不变结构。通过这样做,所有为公共客户执行计算的表示操作都可以抽象到基础中。
在实践中,通常不遵循这些规则,因为它们通常会生成微不足道的包装器,并且需要编写和维护大量额外的代码。所以虚函数通常最终是公开的,即使这在原则上是完全错误的。
在创建函数或类之前,我们应该了解该函数或类的范围,无论它是全局的还是本地的。
例如:“连接字符串()”。每个数据库连接都需要“ConnectionString()”,因此它声明为 Public。
private:只被这个类使用,不被其他类和派生类使用。
protected:被这个类和可能的派生类使用,但不被其他类使用。
public:由其他类、此类和派生类使用。
很难在私有和受保护之间做出选择。因此,如果派生类有 1% 的机会可能需要它,我总是将函数设为 protected。
我将在这里提出一个反驳的论点:而不是创建函数private
,它通常应该是或者protected
或者也virtual
。以API的调用或使用很好(也就是public
功能很好),但是类的实现不够,或者需要扩展的情况。您(当然)会派生基类,并为public
需要更改的公开函数提供新定义。
第一个问题是派生类是否没有足够的权限访问内部变量或辅助函数来实现改进。即,要重写暴露的 api 函数,没有辅助函数是不可能的。借用Nawaz 上面的出色答案,因为我会将一些外部定义的类SomeClass
作为参数传递给public
-ly 暴露的 api void DoWork(SomeClass param)
,并使用辅助函数对其进行验证bool IsValidParam(SomeClass param)
,如果我需要重写DoWork
,如果没有现有的辅助函数,那将是一件痛苦的事情, 如IsValidParam
, 只能作为protected
.
第二个问题是您是否需要覆盖内部函数,而不是重新实现整个面向公众的调用函数。在这种情况下,您可以声明您的内部辅助函数virtual
。另请参阅何时使用 Private Virtual。像上面的例子一样,如果我更改了SomeClass
,或者派生类,或者在不同的上下文中需要不同的(更严格?更宽松?)验证,我可能只覆盖virtual IsValidParam
,而不理会该DoWork
函数。一般来说,如果你可能继承一个类,或者将它用作基础,最好还是让它成为virtual
函数。
public virtual
根据软件的性质或架构的设计,在早期阶段声明所有功能或/然后protected virtual
限制公开的 api可能很有用。然后最后,根据需要拉出virtual
或声明它private
。但是虽然不受限制,但它使下游改进更容易;特别是如果它减少了编辑已经“固定”或难以更改的库的需要。