【问题标题】:Inheritance of friendliness and access levels友好度和访问级别的继承
【发布时间】:2016-12-10 21:52:59
【问题描述】:

我有以下测验问题:

#include <iostream>
using namespace std;

struct A
{
    void ohai() { cout  << "ohai" << endl; }
};

struct C : private A {
    friend int main();
};

struct X : C {};
struct Y : private C {};

int main() {    
    C().ohai()  // OK
    X().ohai(); // OK
    Y().ohai(); // Not OK
}

问题是 C 类是从 A 私有继承的。因此,A 的所有成员,即使是公共的,现在在 C 中都变为私有的。 C 类还声明了与主函数的友好性(这没关系,现在“主”函数可以调用 C 的私有方法)。 然后是类 X 和 Y,它们都是从 C 派生的。 X 公开派生自 C,Y 私下派生自 C。

在“main”函数中,我创建了 C 类的实例并调用从 A 私有继承的“ohai”方法,这很好,因为 main 是 C 的朋友。 然后我创建类 X 的实例并调用 'ohai' - 这出乎意料地有效! 但是当我创建 Y 类的实例时,这不起作用!

我知道继承是不可继承的。所以这不是继承友好性,它允许'main'在类X的对象上调用'ohai'方法。 类 Y 的东西证实了这一点,因为将继承类型更改为私有就足以停止此工作。

我想知道为什么在 X 对象上调用 'ohai' 是可以的。继承不能提升函数的级别(从私有到公共),所以如果 X 从 C 继承,即使是公开的,来自 C 的所有私有方法都应该在 X 中保持私有。由于友好性不可继承,“主”方法不应该能够调用 X 的私有方法。 Y类从C私有继承,这使得C的所有成员在X中都是私有的,但是'ohai'方法在C中已经是私有的(因为C是从A私有继承的)所以这不应该改变任何东西,但不知何故它确实(与 X 相比)。

请帮助我理解这个问题。 此致 优凯

【问题讨论】:

    标签: c++


    【解决方案1】:

    我不完全确定我在这里的答案。如果我错了,请纠正我。

    我相信这里发生的事情是,当您调用 X().ohai() 时,X 中的 this 指针被隐式地转换为它的基类 C,它赋予了 main 友谊。更准确地说,当编译器在X 中查找ohai 时,它没有在X 中找到它,也没有在它的直接基类C 中找到它,而是在A 中找到它。友谊和其他访问控制机制不会干扰编译器应该在哪里找到成员函数。它的效果是在找到成员之后发生的。一旦编译器在A 中找到ohai,它需要将X * 指针(this)转换为A *。 (到目前为止,我很确定我是正确的)。 X 是从 C 继承的公共继承,因此它可以将其转换为 C *。并且Cmain友情,所以它可以继续投给A *。因此它可以调用ohai。 (最后一部分不是很精确,但比较容易理解)。

    例如,尝试以下,

    int main()
    {
        X x;
        Y y;
        static_cast<A *>(&x);
        static_cast<A *>(&y);
    }
    

    第一次转换没有问题,而第二次转换在 Clang 中会出现以下错误,

    test.cpp:24:22: error: cannot cast 'Y' to its private base class 'A'
        static_cast<A *>(&y);
                         ^
    test.cpp:15:11: note: constrained by private inheritance here
    class Y : private C
              ^~~~~~~~~
    

    如果您删除 friend 声明,第一次转换也会失败,但原因不同。

    “友谊不能被继承”是正确的,但它实际上与这里发生的事情没有任何关系

    如果您删除朋友声明,并尝试以下操作,

    int main()
    {
        X x;
        Y y;
        static_cast<A *>(&x);
        static_cast<C *>(&x);
    }
    

    由于访问控制,第一次投射会失败,而第二次会成功。

    与 main 的友谊不仅授予其访问 C 的私有成员的权限,还授予其私有基类的访问权限。对于这个问题,在有和没有朋友声明的情况下尝试以下操作

    int main()
    {
        C c;
        static_cast<A *>(&c);
    }
    

    最后,试试下面的

    struct A {
        void ohai() { std::cout << "ohai" << std::endl; }
    };
    
    struct C : private A {
        using A::ohai;
    };
    
    struct X : C {
        void foo() { C::ohai(); } // static_cast<C *>(this)->ohai();
        void bar() { A::ohai(); } // static_cast<A *>(this)->ohai();
    };
    
    int main()
    {
        X x;
        x.foo();
        x.bar();
        x.ohai();
    }
    

    函数foo 可以,但bar 会失败。因为X 可以转换为直接基址C 并调用ohaiC 中公开。但它不能大小写为A 并在A 中调用ohai

    【讨论】:

    • 谢谢。也许你是对的。我尝试了您的示例,并且在 GCC 中的示例 (1) 的情况下,我在两种情况下都遇到了相同的错误(“错误:'A' 是 'X' 的不可访问的基础”),有和没有友好性声明(与 Clang 相反,根据你写的)。在第二个示例中,如您所写,第一个转换失败并出现与上述相同的错误,第二个工作正常。
    • @YotKay 只是“也许”不要太确定我的答案。我认为它可以解释发生了什么。但是在我说到目前为止,我很确定我是正确的之后,我没有在标准中找到特定的短语。假以时日,其他更知情的人可能会给你更好的答案
    • @YotKay 访问控制是在解决成员函数调用之后出现的,并且成员函数调用将涉及到基类的隐式转换,这一点很清楚。
    • 还有其他答案吗?
    猜你喜欢
    • 1970-01-01
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多