虚拟方法何时应该是纯方法
When should a virtual method be pure?
我找到了一些我正在处理的代码,并且想知道最好的设计实现是什么。
如果基类将方法定义为虚拟方法,但也实现了一个空主体,因此不需要派生类来实现主体,那么它不应该成为纯体吗?
virtual void AMethod1() {} // 1
virtual void AMethod2() {assert(false);} // 2
virtual void AMethod3() = 0; // 3
- 当前代码。
- Idea1:提醒用户此派生对象尚未实现此方法体。
- Idea2:强制派生类实现主体,无论是否为空。
您,值得信赖的了不起的 SO 人员,怎么看?
编辑1:发布(并阅读答案(后,我意识到断言是不好的!
virtual void AMethod3() = {throw (ENotImplemented)}; // 4
这有点取决于你的编码风格有多"纯粹"。有些人认为你应该始终只定义一个只用纯虚函数的接口,并从中派生出所有具体的类。
其他人则更务实,并且相信如果有一个良好的默认实现,则可以将其添加到基类(选项 1(。
第二个选项似乎最没有用,因为它将检测延迟到运行时。大多数程序员宁愿选择选项 3 中的编译错误。
像往常一样,C++支持多种范式,您可以选择自己喜欢的一种。
如果派生类必须实现此方法,则应使用选项 3。如果在派生类中的实现是可选的,请使用选项 1。完全避免选项 2。
如果基类将方法定义为虚拟方法,但也实现了一个空主体,因此不需要派生类实现主体,那么它不应该被设为纯体吗?
这取决于是否要强制派生类重写该方法。如果你这样做,那么使用纯virtual
;正是针对该要求的语言支持。如果以后有或可能在某个时候是 amethod
的默认实现,则使用纯virtual
方法和实现:
class Foo {
virtual void amethod() = 0;
};
void Foo::amethod() {
// whatever
}
该函数现在仍然是纯virtual
因此无法实例化Foo
类,但任何派生类都将继承实现,其方法可以将其调用为 Foo::amethod
。
使方法纯虚拟比使用断言进行默认实现更直观。如果在大多数情况下什么都不做是默认实现,则当前代码会更好。当然,如果你想使用多态性,它应该保持虚拟。
-
virtual void AMethod1() = 0;
:当你的基类没有要提供的实现并且应该实现这种行为时,纯虚拟是最好的。(这是您问题中的选项 3( -
virtual void AMethod1() {}
:当您的基类没有要提供的实现并且可以实现此行为时,具有空实现的虚拟是最好的。(这是您问题中的选项 1( -
virtual void AMethod1() { assert(false); }
:在我看来,必须避免带有assert(false)
的虚拟。我看不出它有任何有效的用途。这背后的基本原理是,上述两个选项涵盖了所有用例:行为可能或应该实现,因此定义总是失败的可调用方法没有用。编译器可以通过阻止此调用来为您处理此问题,因此此选项会将此检查推迟到运行时,从而引入风险。(这是您问题中的选项 2(
我见过很多这样的例子,你需要实例化类,所以您将virtual
与空主体一起使用:
virtual void AMethod1() {} // 1
如果要强制派生类重写,请使用此选项此函数,您不需要默认值:
virtual void AMethod3() = 0; // 3
所以这真的取决于你想做什么。
如果我们不这样做,我们应该使用纯虚函数想要实例化一个类,但让它充当基类对于从它派生的所有类。
一件重要的事情关于纯虚函数需要注意的是,这些必须在所有派生类中重写函数否则,编译将标记错误。
简单例如,
class alpha {
public:virtual void show()=0; //pure virtual function
};
class beta : public alpha {
public:void show() //overriding
{
cout<<"OOP in C++";
}
};
void main() {
alpha *p;
beta b;
p=&b;
p->show();
}
如果基类将方法定义为虚拟方法,但实现一个空 主体,因此不需要派生类实现 一个身体,难道不应该被做成纯净吗?
这取决于您的设计。如果你的方法是纯虚拟的,你正在向派生类开发人员发送一条消息,说">你必须在这里放置一些实际代码,以便你的类工作"。另一方面,如果你的方法是虚拟的,有一个空的主体,则消息是">你可以在这里放置一些代码,但这取决于你的实际需求"。
virtual void AMethod1() {} // 1
virtual void AMethod2() {assert(false);} // 2
virtual void AMethod3() = 0; // 3
我肯定更喜欢选项 3 而不是 2,如果派生类没有实现虚拟方法,它会产生编译错误而不是运行时错误。
由于您需要virtual
机制;以下是我的简短回答:
(1(
virtual void AMethod1() {}
要求:
- Allow creating objects of base class
- Base `virtual` method is use.
(2) virtual void AMethod2() {assert(false);}
要求:
- Allow creating objects of base class
- Base method is not used
- Force derived classes to implement the method (hard way, because it happens at runtime).
(3) virtual void AMethod3() = 0;
要求:
- Don't allow base class object creation
- Force derived classes to implement the method at compile time
没有简单的规则:
如果对某些派生类有意义,请使用 1(空实现(在调用函数时不执行任何操作。
如果派生类不实现功能。
在函数的前提条件为另一个虚函数 Hare 返回 true,该函数具有默认实现返回 false(或类似的东西(。基本上,如果接口的一部分是可选的。 (但通常,它是在这种情况下,最好派生一个接口;实现扩展接口派生自它,客户端希望使用它 dynamic_cast
到扩展接口。
根据经验(但你的编程风格可能不同(,1 似乎至少在 90% 的情况下适用,我认为在 20 多年的C++中,我用过 3 一次,或者两次。
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- 何时应在构造函数参数中使用 const C++?
- 为什么或何时应在调用之前将可调用函数参数强制转换为右值?
- 点云库 (PCL) - 声明点云时何时应使用 ::P tr 的经验法则?
- 何时应使用 C++ 固定宽度整数类型,它们如何影响性能?
- 记录器何时应刷新
- 何时应使用 [[maybe_unused]]
- 何时应在现代C++中使用(非标头)源文件
- 何时应使用模板化参数与构造参数
- 关键部分或静音是否真的是成员变量,或者何时应成为成员变量
- 何时应存储指向函数的引用或指针?
- 何时应在Qt中将子对象声明为其父类的成员变量
- 何时应找到附加到模型的边界框的最小值和最大值
- 何时应删除默认的移动构造函数时令人困惑的事情
- 何时应使用make_heap与优先级队列
- OpenGL:多种渲染方法.何时使用哪个
- 何时应通过常量引用传递运算符重载函数的参数
- 所有公共方法都应与业务逻辑相对应
- 何时应防止隐式销毁?它是如何工作的
- 何时应使用std::atomic_compare_exchange_strong