我用来快速确定派生到基础转换是否合法的规则是检查在转换的上下文中derived
IS-A base
(即{ {1}}可以访问derived
)的公共API
它在C ++ Primer(第5版)中更好地表达为:
对于代码中的任何给定点,如果可以访问基类的
base
成员,则也可以访问派生到基础的转换,而不是其他。
现在让我们假设一个类层次结构如下:
public
似乎违反上述规则的结果是在课程class Base
{
public:
int mem;
};
class Derived : protected Base
{
static void f(Derived *d)
{
d->mem; // OK, in this context, a Derived IS-A Base
Base *b = d;
}
};
int main()
{
Derived d;
//d.mem; // Compilation error : in this context a Derived IS-NOT-A Base
//Base *b = &d; // Compilation error too : consistent with the intuitive rule
return 0;
}
class Derived_Derived : public Derived
{
static void f(Derived *d)
{
//d->mem; // Compilation error : in this context a Derived IS-NOT-A Base (as expected)
Base *b = d; // COMPILATION OK : which seems to violate the rule above
}
};
中,功能Derived_Derived
:
编译器接受从f
到d
的转换。
在此上下文中,由于b
继承,因此无法通过指向Base
的指针访问Derived
定义的公共API。然而,派生到基础的转换有效。
在一些编译器上进行了测试以得到相同的结果,因此我认为它是标准定义的行为。允许这样做的理由是什么?
Arne Vogel 的回答证实这是一个标准定义的行为,带有相关的摘录和解释。
然而,我无法找到§11.2/ 4条件#3背后的基本原理(授权最后一次转换),这通常有助于记住规则。
有关理由的问题仍未解决。
答案 0 :(得分:5)
C ++ Primer的作者巧妙地错误引用了ISO C ++标准。引自N3376(C ++ 11的后期/最终工作草案):
§11.2/ 4 {em> R 可以访问
B
的基类N
,如果- [{1}}的发明公共成员将成为
B
的公共成员,或- R 出现在班级
的私人或受保护成员N
的成员或朋友身上,以及发明的公众N
的成员将是B
或- R 发生在从
N
派生的类P
的成员或朋友中,并且 发明N
的公共成员将是私人或受保护的成员B
或- 存在一个类
P
,以便S
是B
可访问的基类 在 R 和S
是S
的基类,可在 R 处访问。
在您给出的示例中,第三个条件成立,基类可访问。继续...
5如果可以访问基类,则可以隐式转换指针 到派生类到指向该基类的指针(4.10,4.11)。
这意味着标准允许隐式转换,兼容编译器必须支持它。
如果来自C ++ Primer的文本是法律,那么基类转换应该同等地禁止访问N
,因为无法访问发明的公共成员(参见§11.2/ 5,§11.4)超过mem
。然而,这并不是标准所说的“发明的公共成员”。在上述§11.2/ 4的条件#3中,它表示如果发明的公共成员是“P的私人或受保护成员”,则可以访问基类,这显然就是这种情况。它不要求所述发明的私人或受保护成员实际可访问。
答案 1 :(得分:-2)
您使用受保护的继承来定义Derived类。因此,Base类的公共成员在Derived类中受到保护。因此,您无法从类外部访问基本成员变量,如d.m或d-> m。 但是可以从成员函数访问它们,因为成员函数没有限制访问类的私有或受保护成员。访问限制仅适用于课堂以外的世界。
class Derived_Derived : public Derived
{
static void f(Derived_Derived *d)
{
d->mem; // Will work
Base *b = d; // COMPILATION OK : which seems to violate the rule above
}
};
class Derived_Derived : public Derived
{
static void f(Derived *din)
{
d = (Derived_Derived *) din; // Will work too, of course use dynamic_cast when if class is polymorphic
d->mem; // Will work
Base *b = d; // COMPILATION OK : which seems to violate the rule above
}
};
归结为一个简单的规则。所有成员private / protected / public都可以访问其成员函数。考虑Derived_Derived中的函数f,它将可以访问传递给它的任何Derived_Derived类对象的所有成员。因为在Derived_Derived :: f函数的上下文中,将应用特定于Derived_Derived类的访问限制,并允许访问Derived_Derived的所有成员。 如果Derived_Derived :: f采用Derived类型的参数,则在f的上下文中,私有或受保护的成员将不可访问,因为在传递的对象上将应用特定于Derived类的访问限制并且无关紧要如果Derived_Derived派生自Derived。