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

2019-05-10 21:36:03 +08:00
 lhx2008

我在 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 {} 更清晰。

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

这两者 private 还是 protected 只在多重继承中会产生区别。
按照你的示例代码举个例子:
GameCharacter 派生出一个类 RPGGameCharacter。
RPGGameCharacter 这个类的成员函数可以自由的使用 doHealthValue()
RPGGameCharacter 又派生出一个 MMORPGGameCharacter。
MMORPGGameCharacter 这个类的成员函数则不能使用 doHealthValue,因为 doHealthValue 是 private 的,如果声明的时候标记为 protected 那么就可以。
secondwtq
2019-05-10 23:48:26 +08:00
@wevsty 这个是正解(虽然这个貌似不叫“多重继承”
wevsty
2019-05-11 00:31:28 +08:00
@secondwtq
嗯,可能叫多继承,或者多层继承比较准确。
q8515620
2019-05-11 01:37:27 +08:00
@wevsty “多继承”是另一个概念,指一个子类同时继承多个父类。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/563000

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX