能否将NVI基类虚函数设为私有且仅允许派生类在重写时调用?
问题描述
我有一个通过虚函数提供定制点的NVI接口,这些虚函数仅用于NVI内部定制,绝不允许在其上下文之外被派生类显式调用。
将这些函数设为private可以阻止派生类在外部显式调用(符合预期),但也导致派生类无法在重写该函数时调用基类的默认实现(不符合预期)。我知道可以通过注释说明“绝不在重写函数外调用基类函数”,但这仍留有违反接口规范的余地。
示例代码如下:
class nvi { public: void operator()(int i) const { // do stuff _customization(i); // other stuff } protected: ~nvi() = default; private: virtual void _customization(int) const {} }; class custom final : public nvi { public: void some_unrelated_func() { // this should not be possible // base::_customization(815); } private: void _customization(int i) const override { // do custom stuff // this should be possible nvi::_customization(i); } };
核心问题:是否可以将基类中的虚函数设为私有,同时允许派生类仅在重写该函数时调用基类的默认实现?
注:这是一个XY问题,但X不在我的控制范围内。我个人认为,当函数不修改状态或无可见副作用时,设为protected是可行的,即便派生类在其他地方显式调用也无妨。同时,可行的解决方案不能比添加规范使用的简单注释更复杂。
解决方案
有两种简单可行的方案满足需求:
方案一:protected虚函数+明确命名约定
将基类的定制虚函数设为protected,但通过统一的命名规则(比如前缀do_或internal_)明确标识这是NVI的内部定制点,仅允许在重写函数内部调用基类版本。这种方式实现成本极低,和注释复杂度相当,但通过命名规范能更清晰地约束调用场景。
方案二:私有虚函数+protected非虚辅助函数
这是更严谨的实现方式,完全满足“私有虚函数阻止外部调用,仅允许重写时调用基类实现”的需求,且复杂度不高:
- 在基类中保留私有虚函数
_customization,同时新增一个protected的非虚辅助函数,内部直接调用私有虚函数; - 派生类在重写私有虚函数时,通过调用这个辅助函数来触发基类的默认实现,而在其他成员函数中无法直接访问基类的私有虚函数。
修改后的代码示例:
class nvi { public: void operator()(int i) const { // do stuff _customization(i); // other stuff } protected: ~nvi() = default; // 辅助函数,仅用于派生类重写时调用基类默认实现 void call_base_customization(int i) const { _customization(i); } private: virtual void _customization(int) const {} }; class custom final : public nvi { public: void some_unrelated_func() { // 仍然无法调用基类的私有_customization // nvi::_customization(815); } private: void _customization(int i) const override { // do custom stuff call_base_customization(i); // 合法调用基类默认实现 } };
这个方案既保证了私有虚函数不会被派生类外部调用,又给重写函数提供了调用基类默认实现的合法途径,实现简单且无额外复杂度。
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

