为什么在派生类中调用受保护的静态方法是被允许的?

13
在派生类中调用受保护的构造函数是不允许的,正如这里所解释的那样。
接受的答案解释了当基类A的对象是类B的子对象时,protected访问修饰符才会授予对A对象成员的访问权限。到目前为止没问题。
但是,为什么(至少在GCC 4.6.3)可以调用静态 protected 方法呢?特别是,下面的代码能编译通过,然而被注释的那行却不能:
class A 
{
protected:
    A() {}
    static A makeA() { return A(); }
};

class B: public A
{
public:
    static A makeAFromB()
    {
        return makeA(); // compiles
        // return A();  // does not compile
    }
};

从哲学上讲,构造函数在很大程度上就像返回类 A 对象的静态方法,这就是为什么我不理解这里行为上的区别。


3
你应该将 return A::A() 改成 return A()。虽然仍无法编译,但这次是由于protected访问相关的原因,这使问题更加清晰。 - interjay
1
你是完全正确的,已经编辑好了。 - Boris Dalstein
3个回答

12
但是,为什么允许调用静态保护方法呢(至少在GCC 4.6.3中)?
因为标准规定了这样做是可以的。对于受保护成员的可访问性所适用的约束条件(并且您链接的答案已经很好地解释了),在C++11标准的第11.4/1段中定义,其中第一句指定:
“当非静态数据成员或非静态成员函数是其命名类的受保护成员(11.2)时,应用超出第11条款中早先描述的其他限制的附加访问检查。”
附加访问检查不适用于静态成员或静态成员函数。

好的,感谢澄清!(以及引用规范中指定“非静态”的部分) - Boris Dalstein

2
您说得对,只授予访问嵌入基础对象的方式可能令人惊讶...但这是有原因的。假设您有几个派生自Base的类,并且其中一个类中的方法想要通过某些引用或指针对任意Base对象进行操作...它可能会使其与其他派生链的一部分相关联的Base混淆,而该链具有对Base对象给出行为期望的其他行为。实际上,您正在删除其他派生类打算实现的封装,防止它执行预期的不变量,走出良好OO行为的世界...将此见解应用于您的特定情况,C++规则防止您使用受保护的构造函数来创建预计由某个派生类监管不变量的子对象,而实际上并没有派生对象将该Base对象嵌入其中。
此外,构造函数的protected方式具有明显的实用性——它防止使用该构造函数构造类(除了作为派生类的子对象或基类的成员函数(例如您示例中的makeA)),无论它们是静态还是非静态成员函数都是无关紧要的...所有成员函数始终都可以完全访问类。
忽略静态成员函数的直观含义有什么用处?如果您希望它们实际上是私有的,那么只需将它们设置为私有/如果您需要访问权限,请将它们保护。显然,受保护的访问是根据我第一段中的理由受限制的。

谢谢你的回答 :) 实际上,我期望的是从Derived中调用Base的受保护构造函数。当我意识到这是不可能的时,我尝试使用受保护的makeA,因为我原来期望它能够工作,但现在不确定了,因为我的先前期望被证明是错误的(而这种简单的黑客提供了完全相同的功能)。但是,你对我的问题标题是正确的:显然,受保护的静态方法应该在派生类中可调用,否则就没有与私有静态方法的区别了。 - Boris Dalstein
@Boris:“预期的是从Derived中可以调用Base的受保护构造函数” - 是的,我也有这样的经历! :-) - 希望上面的推理有助于解释“为什么不”的方面。 makeA的关键区别在于实现仍然在A的控制下...它被允许决定A的封装边界,而让派生类使用构造函数则放弃了控制权。干杯。 - Tony Delroy
以上的推理确实有所帮助,以及您的额外评论 :) 我仍然很烦恼我不能调用解构函数,但是好吧,至少有一个很好的理由;-) - Boris Dalstein

0

静态方法是被继承的,这意味着它们可以在子类的方法中使用。我们可以使用子类的名称调用静态方法,也可以使用子类的实例进行调用,或者在子类的主体中不加限定地调用。


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