sil*_*rgh 118 c++ polymorphism access-specifier
在C++中创建私有方法虚拟的优点是什么?
我在开源C++项目中注意到了这一点:
class HTMLDocument : public Document, public CachedResourceClient {
private:
virtual bool childAllowed(Node*);
virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};
Run Code Online (Sandbox Code Playgroud)
Spe*_*cer 10
尽管所有要求将虚拟成员声明为私有的呼吁,但这个论点根本就没有用水.通常,派生类重写虚函数必须调用基类版本.如果声明它就不能private
:
class Base
{
private:
int m_data;
virtual void cleanup() { /*do something*/ }
protected:
Base(int idata): m_data (idata) {}
public:
int data() const { return m_data; }
void set_data (int ndata) { m_data = ndata; cleanup(); }
};
class Derived: public Base
{
private:
void cleanup() override
{
// do other stuff
Base::cleanup(); // nope, can't do it
}
public:
Derived (int idata): base(idata) {}
};
Run Code Online (Sandbox Code Playgroud)
您必须声明基类方法protected
.
然后,你必须采取丑陋的权宜之计,通过注释表明该方法应该被覆盖但不被调用.
class Base
{
...
protected:
// chained virtual function!
// call in your derived version but nowhere else.
// Use set_data instead
virtual void cleanup() { /* do something */ }
...
Run Code Online (Sandbox Code Playgroud)
因此Herb Sutter的指导方针#3 ......但无论如何,这匹马已经离开了谷仓.
当您声明某些内容时,protected
您隐含地信任任何派生类的编写者以理解并正确使用受保护的内部,只是friend
声明意味着对private
成员的更深信任.
因违反该信任而遭受不良行为的用户(例如,通过不打扰阅读您的文档而标记为"无能为力")只能归咎于自己.
更新:我收到了一些反馈,声称您可以使用私有虚拟功能以这种方式"链接"虚拟功能实现.如果是这样,我肯定希望看到它.
我使用的C++编译器肯定不会让派生类实现调用私有基类实现.
如果C++委员会放宽了"私有"以允许这种特定访问,那么我将全部用于私有虚拟功能.目前,我们仍被建议在马被盗后锁上谷仓门.
我在阅读Scott Meyers的"Effective C++"时第一次遇到这个概念,第35项:考虑虚拟功能的替代方案.我想引用Scott Mayers给其他可能感兴趣的人.
它是通过非虚拟接口习语的模板方法模式的一部分:面向公众的方法不是虚拟的; 相反,它们包装了私有的虚方法调用.然后,基类可以在私有虚函数调用之前和之后运行逻辑:
public:
void NonVirtualCalc(...)
{
// Setup
PrivateVirtualCalcCall(...);
// Clean up
}
Run Code Online (Sandbox Code Playgroud)
我认为这是一个非常有趣的设计模式,我相信你可以看到增加的控件是如何有用的.
private
?最好的理由是我们已经提供了一种public
面向方法.protected
一点,以便我可以将该方法用于其他有趣的事情?我想它总是取决于你的设计以及你如何相信基类适合.我认为派生类制造者应该专注于实现所需的逻辑; 其他一切都已经得到了解决.此外,还有封装问题.从C++的角度来看,覆盖私有虚拟方法是完全合法的,即使您无法从类中调用它.这支持上述设计.
小智 5
我使用它们来允许派生类“填补基类的空白”,而不会将这样的漏洞暴露给最终用户。例如,我有从公共基础派生的高度有状态的对象,它只能实现整个状态机的 2/3(派生类根据模板参数提供剩余的 1/3,并且基础不能是其他原因)。
我需要拥有公共基类才能使许多公共 API 正常工作(我正在使用可变参数模板),但我不能让该对象随意使用。更糟糕的是,如果我将状态机中的坑以纯虚拟函数的形式留在“Private”之外的任何地方,我就会允许从其子类之一派生的聪明或无知的用户覆盖用户永远不应该接触的方法。因此,我将状态机“大脑”放在私有虚拟函数中。然后,基类的直接子类填充其非虚拟覆盖上的空白,用户可以安全地使用生成的对象或创建自己的进一步派生类,而不必担心弄乱状态机。
至于你不应该拥有公共虚拟方法的论点,我说的是废话。用户可以像公共虚函数一样轻松地不正确地覆盖私有虚函数——毕竟他们正在定义新的类。如果公众不应该修改给定的 API,那么就不要在可公开访问的对象中将其设为虚拟。