在C ++中,编码人员不知道其他编码人员是否会继承他的班级。他应该将该课程中的所有功能都虚拟化吗?有什么缺点吗?或者它根本不被接受?
答案 0 :(得分:10)
在C ++中,只有在打算以多态方式使用它时,才能使类可继承。在C ++中处理多态对象的方式与处理其他对象的方式有很大不同。您不倾向于将多态类放在堆栈上,或者通过值传递它们或从函数返回它们,因为这会导致切片。多态对象倾向于堆分配,传递并通过指针或引用等返回
如果你设计一个不继承的类然后从它继承,你会导致各种各样的问题。如果析构函数未标记为虚拟,则无法通过基类指针删除该对象,而不会导致未定义的行为。如果没有标记为virtual
的成员函数,则无法在派生类中重写它们。
作为C ++的一般规则,在设计类时,请确定是否要继承它。如果这样做,请标记相应的函数virtual
并为其提供virtual
析构函数。您也可以禁用复制赋值运算符以避免切片。同样,如果您希望该类不可继承,请不要给它任何这些函数。在大多数情况下,从一个未设计为继承的类继承是一个逻辑错误,并且大多数时候你想要这样做,你通常可以使用组合而不是继承达到这个效果。
答案 1 :(得分:4)
在C ++中,您设计您的类,可以用作值类型或多态类型。例如,请参阅C++ FAQ。
答案 2 :(得分:4)
不,通常不会。
非虚函数强制执行类不变行为。虚函数没有。因此,编写基类的人应该考虑特定函数的行为是否应该是类不变的。
虽然设计的可能允许所有行为在派生类中有所不同,但这是相当不寻常的。编写课程的人要么对其设计没有太多考虑,而且缺乏做出决定的决心,这通常是一个很好的线索。
答案 3 :(得分:2)
如果你正在制作一个供其他人使用的课程,你应该在你的界面中加入大量的思考,并尝试弄清楚如何使用你的课程。然后决定哪些函数应该是虚拟的。
或者更好的是为您的类编写测试用例,使用它如何使用它,然后使接口工作。你发现这样做会让你感到惊讶。你认为绝对必要的东西可能很少需要,而你认为不会被使用的东西可能会成为最有用的方法。这样做可以节省您长时间不做不必要工作的时间,最终得到可靠的设计。
答案 4 :(得分:2)
Jerry Coffin和Dominic McDonnell已经涵盖了最重要的一点。
我只是添加一个观察,在MFC时代(20世纪90年代中期),由于缺乏与事物挂钩的方式,我非常恼火。例如,文档建议复制MFC的源代码以进行打印和修改,而不是覆盖行为。因为那里什么都没有。
当然有多种方法提供“钩子”,但虚拟方法是一种简单的方法。在设计糟糕的类中需要它们,因此客户端代码可以解决问题,但在那些设计糟糕的类中,方法不是虚拟的。对于具有更好设计的类,没有太多需要覆盖行为,因此对于那些使方法默认为虚拟(并且非虚拟仅作为活动选择)的类可能适得其反;正如杰瑞所说,虚拟化为衍生类提供了机会,搞砸了。
有一些设计模式可以用来减少搞砸的可能性。
例如,通过健全性检查将内部虚拟包装在公开的非虚方法中,例如,使用解耦事件处理(在适当的情况下)而不是虚拟。
干杯&第h。,
答案 5 :(得分:1)
当您创建一个类,并且希望以多态方式使用该类时,您必须考虑该类具有两个不同的接口。用户界面由基类中可用的公共函数集定义,并且应该涵盖用户希望对类的对象执行的所有操作。此接口由访问限定符定义,特别是public
限定符。
还有第二个接口,用于定义如何扩展类。在该级别,您必须考虑通过扩展类来覆盖要覆盖的行为,以及要为扩展类提供的对象的哪些元素。您可以通过protected
限定符提供对派生类的访问,并通过虚函数提供扩展点。
您应尽可能尝试遵循非虚拟接口惯用法。这个成语(google for it)基本上试图通过不使用公共虚拟功能来完全分离这两个接口。用户调用非虚函数,然后通过受保护/私有虚函数调用可配置的功能。这清楚地将扩展点与类接口分开。
有一个案例,其中virtual
必须是用户界面的一部分:析构函数。如果您希望为用户提供通过指向基础的指针来销毁派生对象的能力,那么您必须提供虚拟析构函数。另外,您只需提供受保护的非虚拟文件。
答案 6 :(得分:0)
他应该按原样对函数进行编码,他不应该像在你指定的情况下那样使它们成为虚拟函数。
原因是 1 GT; CLASS CODER显然会使用他正在使用的功能。 2 - ;继承的类可能会也可能不会根据要求使用这些函数。 3 GT;任何函数都可以在派生类中被覆盖而没有任何错误。