如何在派生类中,通过基类的实例调用基类中受保护的成员函数?

4
我们可以在这里这里找到为什么我们不能在派生类的方法中从基对象调用基类的受保护方法的解释,就像在这段代码中一样:
class B {
protected:
    void f ();
};

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

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


在另一个类中调用私有方法时,答案相同。将其设为公共或友元,或者再次考虑是否真的需要调用它? - Bryan Chen
你尝试过友元类吗? - Stefan
1
如果这是一个学术问题,你可以使用友谊来解决它。但如果这是来自于实际应用,你需要质疑你的设计。找出一个更好的设计,不要让自己陷入这种困境。 - R Sahu
“friend” 的问题在于模块化。B 不必知道哪些类将会继承它并使用其受保护的函数。这不是一个具体的问题,因为我知道我可以采用不同的设计。但在我的情况下,我认为没有反对以这种方式调用基类函数的理由。 - Félix Faisant
3个回答

3

注意:启用此功能的直接(且推荐)方法是将继承类设为基类的friend(友元)类;这意味着它将可以访问其protected(受保护的)private(私有的)部分。


我想打破规则

所以你决定标准制定的规则很烦人,你想在任何想要的时间,以任何你想要的方式做任何你想做的事情!规则是用来打破的,等等。

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
我们的黑科技之一就是取D::f的地址,因为它实际上是指向B的成员函数指针,我们使用该地址调用other上的函数。
另外一种(语义上等价的)编写代码的方法:
void (B::*hack)() = &D::f; (other->*hack) ();

注意:从技术上讲,我们并没有深入访问B的保护内容,我们只是依赖于通过D::f访问的内容恰好与B::f中的内容相同这一事实。我们仍然遵循标准规定的规则,只是有些滥用了它们。


2
谢谢!我认为你的解决方案非常干净,因为它不需要修改类层次结构,这与 friend 解决方案不同。 - Félix Faisant
1
我在想这里是否存在未定义(或至少未指定)的行为。我会期望它,因为我们更多地是在类型为“B”的对象上调用类型为“D”的方法。另一方面,我猜即使存在未定义行为,这也很有可能总是按预期工作。 - Ignitor

1
更好的解决方案是在Base中声明一个静态受保护的函数,将调用重定向到私有/受保护的函数。这样,我们不会破坏封装性,因为Base的设计者可以明确选择允许所有派生类相互调用foo,同时避免将foo放入公共接口或显式地将所有可能的Base子类转换为友元。
请参见我的原始问题答案,其中包含代码示例。

0
你可以将 class D 声明为 class Bfriend:
class B {
friend class D;
protected:
    void f ();
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接