V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
lhx2008
V2EX  ›  问与答

C++ 为什么会有 private virtual 函数,这样写好吗?

  •  
  •   lhx2008 · 2019-05-10 21:36:03 +08:00 · 2741 次点击
    这是一个创建于 2030 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我在 Effective C++ 条款 35 里面看到这种模板模式的写法,有点困惑:

    class GameCharacter {
    public:
        int healthValue() const {
            // do something
            return doHealthValue();
            // do something
        }
    
    private:
        virtual int doHealthValue() const = 0;
    };
    
    

    我从 Java 转过来,Java 里面是没有 private abstract 的,一般模板模式的写法都是 protected abstract ,因为通常来说,private 的函数就是基类内部的函数,派生类也无权知晓,也不用提 overwrite

    那么,在 C++ 里面,是不是也写 protected virtual 比较好呢 ,功能上应该区别不大

    stackoverflow 上也有人讨论,

    有人说

    Prefer to make virtual functions private.

    This lets the derived classes override the function to customize the behavior as needed, without further exposing the virtual functions directly by making them callable by derived classes (as would be possible if the functions were just protected). The point is that virtual functions exist to allow customization; unless they also need to be invoked directly from within derived classes' code, there's no need to ever make them anything but private.

    确实也有些道理,但是也只有在基类被直接使用的时候有用。

    否则,protected: virtual int doHealthValue() const = 0 似乎比 private: virtual int doHealthValue() const {} 更清晰。

    13 条回复    2019-05-11 01:37:27 +08:00
    wevsty
        1
    wevsty  
       2019-05-10 22:01:27 +08:00
    这个东西是纯虚函数,只有定义,没有实现,这种类是专门用来继承的,并不能直接产生实例,一般用来设计抽象的接口。
    lhx2008
        2
    lhx2008  
    OP
       2019-05-10 22:05:35 +08:00
    @wevsty 是不是纯虚不影响,都可以通过编译的
    wevsty
        3
    wevsty  
       2019-05-10 22:16:03 +08:00
    @lhx2008
    如果你不去使用(产生实例)那么编译是可以过的,但是这代码也没有任何作用。
    如果你要直接定义一个实例出来,编译器就会告诉你错误了。
    比如 GCC 会给提示

    <source>:14:19: error: cannot declare variable 'g' to be of abstract type 'GameCharacter'

    14 | GameCharacter g;

    | ^~~~

    <source>:1:7: note: because the following virtual functions are pure within 'GameCharacter':

    1 | class GameCharacter {

    | ^~~~~~~~~~~~~

    <source>:10:17: note: 'virtual int GameCharacter::doHealthValue() const'

    10 | virtual int doHealthValue() const = 0;
    ccpp132
        4
    ccpp132  
       2019-05-10 22:27:05 +08:00 via Android   ❤️ 1
    没必要,写成 protected 就是了。
    当然如果 team 里面有约定,或者项目有现成的习惯就保持一致
    chinuno
        5
    chinuno  
       2019-05-10 22:28:17 +08:00 via Android
    c 艹的虚基类(带有纯虚函数)相当于接口,子类继承就是实现接口。
    实际上就是在运行时把接口的实现替换成之类中的实现。使用的时候不用子类对象,相当于调用接口,所以并不需要 protected 来表示子类访问的基类对象,这个虚函数就是接口类自己的私有成员。
    tldr:对于接口来说 protected 没意义,除非你不把他当接口,而是普通的类来用
    lhx2008
        6
    lhx2008  
    OP
       2019-05-10 22:28:26 +08:00
    @wevsty 用的话,如果是非纯虚,是可以用的。纯虚当然不行了。现在不是讨论这个问题,现在是讨论到底是用 private 函数 protected
    lhx2008
        7
    lhx2008  
    OP
       2019-05-10 22:31:01 +08:00
    @chinuno 如果用 Java 里面的概念,这个并非“接口”,而是类似 Java 里面的 protected abstract function,基类希望派生类实现一个内部使用的函数名称
    chinuno
        8
    chinuno  
       2019-05-10 22:42:50 +08:00 via Android   ❤️ 1
    @lhx2008 语言上概念的差异吧。不过做的应该是一样的事。c 艹标准没有规定该用 private 还是 protected,都能用,看个人喜好。只是从我的角度来看 private 更合理些
    thedrwu
        9
    thedrwu  
       2019-05-10 22:47:49 +08:00 via Android   ❤️ 1
    我也喜欢这么用,然而写这个函数的单元测试时不如 protected 方便
    wevsty
        10
    wevsty  
       2019-05-10 22:53:49 +08:00   ❤️ 1
    @lhx2008
    用 private 还是 protected 完全取决于你自己的需要啊,这一点是不要纠结的。
    基类中标记为 private 的纯虚函数是没有实现的,你要继承这样的基类就必须自己实现一个与纯虚函数同名的函数。也就是实际上可以理解为是派生类内部独立声明的一个有 private (或者其他属性)的函数,只是与基类中要求的同名而已。所以无论是哪种属性派生类中纯虚函数的实现是可以被派生类自己访问的。

    这两者 private 还是 protected 只在多重继承中会产生区别。
    按照你的示例代码举个例子:
    GameCharacter 派生出一个类 RPGGameCharacter。
    RPGGameCharacter 这个类的成员函数可以自由的使用 doHealthValue()
    RPGGameCharacter 又派生出一个 MMORPGGameCharacter。
    MMORPGGameCharacter 这个类的成员函数则不能使用 doHealthValue,因为 doHealthValue 是 private 的,如果声明的时候标记为 protected 那么就可以。
    secondwtq
        11
    secondwtq  
       2019-05-10 23:48:26 +08:00
    @wevsty 这个是正解(虽然这个貌似不叫“多重继承”
    wevsty
        12
    wevsty  
       2019-05-11 00:31:28 +08:00
    @secondwtq
    嗯,可能叫多继承,或者多层继承比较准确。
    q8515620
        13
    q8515620  
       2019-05-11 01:37:27 +08:00 via Android
    @wevsty “多继承”是另一个概念,指一个子类同时继承多个父类。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2106 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:32 · PVG 08:32 · LAX 16:32 · JFK 19:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.