You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:02:32