如何从 Derived 内部调用 Base 实例中的受保护成员函数?

Posted

技术标签:

【中文标题】如何从 Derived 内部调用 Base 实例中的受保护成员函数?【英文标题】:How to call protected member-function in Base, on instance of Base, from within Derived? 【发布时间】:2014-05-27 23:21:57 【问题描述】:

我们可以找到here 和here 解释为什么我们不能从派生类的方法中调用基类的基类的受保护方法,就像这段代码一样:

class B 
protected:
    void f ();
;

class D : public B 
public:
    void g (B* other) 
        other->f(); /* Error: B::f() is protected */
    
;

但是有什么解决办法吗?如果我们真的想在这个对象上调用这个基方法怎么办?毕竟这样做不会有任何问题,我们可以确保other真的是B

【问题讨论】:

与在另一个类上调用私有方法的答案相同。让它公开或成为朋友,或者再想一想你真的需要打电话给它吗? 你尝试过朋友班吗? 如果这是一个学术问题,你可以用友谊来解决它。如果这是来自实际应用程序,您需要质疑您的设计。找出一个更好的设计,不会让你陷入这个修复。 friend 的问题是模块化。 B 不必知道哪些类将继承它并使用它的受保护函数。这并不是一个真正的共同问题,因为我知道我可以采用不同的设计。但就我而言,我不反对像这样调用基类函数。 【参考方案1】:

注意:启用某些东西的直接(和推荐)方法是使继承的类成为基类的friend;这意味着它将可以访问它的protectedprivate 部分。


我想稍微改变一下规则

因此,您认为标准制定的规则很烦人,您想随心所欲,随心所欲,随心所欲!规则是用来打破的,等等。

class B 
  protected:
    void f ();
;

class D : public B 
  public:
    void g (B * other) 
      (other->*&D::f) (); // legal
    
;

这个合法且功能齐全的黑客如何工作?

尽管标准规定我们不能从D 中检查B::f,但我们当然可以查看D::f;这是相同的(继承的)事情,因为我们没有在 D 内声明另一个 f

我们的 hack 包括获取 D::f 的地址,依赖于它的类型确实是一个 指针,指向 B 内的一个 成员函数 ,并使用该地址调用other上的函数。

另一种(语义上)等效的 sn-p 编写方式:

void (B::*hack)() = &D::f; (other->*hack) ();

注意:从技术上讲,我们并没有深入研究B 的受保护内容,我们只是依赖于通过D::f 访问的内容恰好与B::f 中的那个。我们仍在按照标准制定的规则行事,只是我们在某种程度上滥用了它们。

【讨论】:

谢谢!我发现您的解决方案非常干净,因为它不需要修改类层次结构中的任何内容,这与 friend 解决方案不同。 我想知道这是否存在未定义(或至少未指定)的行为。我会期待它,因为我们或多或少地在B 类型的对象上调用D 类型的方法。另一方面,我想即使是 UB,它也很有可能总是按预期工作。【参考方案2】:

更好的解决方案是在 Base 中声明一个静态受保护函数,将调用重定向到私有/受保护函数。这样,我们就不会破坏封装,因为 Base 的设计者可以做出明确的选择,允许所有派生类相互调用 foo,同时避免将 foo 放入公共接口或显式地将 Base 的所有可能的子类变成朋友.

参见my answer for the original question 代码示例。

【讨论】:

【参考方案3】:

您可以将class D 设为friend of class B

class B 
friend class D;
protected:
    void f ();
;

【讨论】:

以上是关于如何从 Derived 内部调用 Base 实例中的受保护成员函数?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 类层次结构中“前置”的行为

如何从包含基类指针的容器中调用派生类函数(基于其类型)?

模仿`super`关键字:在实例化期间设置`base`类和`derived`类字段

从基类调用派生类的隐藏(非虚拟)方法

试图从 Derived* 的向量中分配 Base* 的向量

无法从Base转换为Derived