Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop Override方法-调用重写的实现(super)还是不调用?_Oop_Inheritance - Fatal编程技术网

Oop Override方法-调用重写的实现(super)还是不调用?

Oop Override方法-调用重写的实现(super)还是不调用?,oop,inheritance,Oop,Inheritance,这个决定有什么经验法则吗?我一直坚持这个问题。即使我知道目前我不需要重写方法的结果,我如何确保将来不会修改重写方法?例如,我正在扩展的父类的作者可能会决定在我覆盖的方法中实现一些副作用,如果没有副作用,对象的状态将不正确。没有硬性规定。有时您不想调用超类实现,有时您想调用超类实现——在调用超类之前和/或之后做一些工作。IMO,默认的决定应该始终是调用超类,然后处理任何不同的行为。这是出于您提到的所有原因(现在可能需要,但如果没有,将来可能需要) 一个例外是,您提前知道超类将做您特别不想做的事情。

这个决定有什么经验法则吗?我一直坚持这个问题。即使我知道目前我不需要重写方法的结果,我如何确保将来不会修改重写方法?例如,我正在扩展的父类的作者可能会决定在我覆盖的方法中实现一些副作用,如果没有副作用,对象的状态将不正确。

没有硬性规定。有时您不想调用超类实现,有时您想调用超类实现——在调用超类之前和/或之后做一些工作。

IMO,默认的决定应该始终是调用超类,然后处理任何不同的行为。这是出于您提到的所有原因(现在可能需要,但如果没有,将来可能需要)


一个例外是,您提前知道超类将做您特别不想做的事情。但这些问题都指向了设计问题。看到子类不调用其父类是非常可疑的。

STL库在虚拟函数的默认实现中通常不做任何非琐碎的工作。它们通过将对虚拟函数的调用包装为非虚拟函数来解决问题。这种行为假定基类的扩展程序永远不会调用虚拟函数的默认实现(因为不需要这样做)

如果基类的设计者决定扩展某个虚拟成员函数的功能,他应该编写这样的包装器,在那里添加新功能和对虚拟函数的调用,并在任何地方使用该包装器。这个概念允许扩展基类的功能和接口,而不会破坏后代类。 例如,请参见std::streambuf


通过这种方式,基类设计器也可以实现Alexandrescu关于不公开虚拟函数的建议。

在重写基类的某个方法时调用基类的实现有两个主要原因:

您希望扩展基类的行为。 在这种情况下,依赖基类来执行工作的主要部分通常比较容易,只需在上面添加额外的工作。这个决定通常很容易做出

基本实现有一些期望的或必需的副作用。 这有时有点难以确定。外部副作用有望被记录下来,你应该能够确定它们是否应该发生

你提到的有时很难确定的是,一个函数是否有内部副作用。也就是说,如果它修改了某个私有状态。一个简单的例子:

等级车{
布尔工程;
公共虚拟空间StartEngine(){
转动点火钥匙();
engineRunning=true;//这是内部副作用
}
公众闲逛{
如果(!引擎规划)
抛出新的InvalidOperationException(“您必须先启动发动机。”);
//实施绕行行驶
}
}
S2000类:汽车{
公共覆盖无效StartEngine(){
按下开始按钮();
}
}
在本例中,由于
S2000
StartEngine()
的实现没有调用
Car
的实现,因此
DriveAround()
将失败。如果没有
Car
的源代码或非常好的文档,
S2000
的作者可能不知道应该调用base的
StartEngine
例程

因此,
Car
不是理想的实现。最好是这样:

等级车{
布尔工程;
public void StartEngine(){//不是虚拟的
StartEngineInternal();
引擎规划=真;
}
受保护的虚拟void StartEngineInternal(){
转动点火钥匙();
}
}
在本例中,内部副作用通过包含在调用受保护的虚拟核心实现的不可重写包装器函数中而得到保护。这样,对象的状态就不依赖于调用基方法的继承者,而将是否这样做的决定权留给它所属的继承者