请考虑以下代码段:
struct Base
{
virtual ~Base() {}
virtual void Foo() const = 0; // Public
};
class Child : public Base
{
virtual void Foo() const {} // Private
};
int main()
{
Child child;
child.Foo(); // Won't work. Foo is private in this context.
static_cast<Base&> (child).Foo(); // Okay. Foo is public in this context.
}
这是合法的C ++吗? “这”正在改变派生类中虚函数的访问模式。
答案 0 :(得分:21)
这是合法的C ++,§11.6/ 1说:
在呼叫点检查访问权限 使用所用表达式的类型 表示对象 调用成员函数(B *中的 上面的例子)。访问的 类中的成员函数 它已定义(示例中为D) 通常不知道。
正如您所指出的那样,Child::Foo()
仍然可以通过基类访问,这在大多数情况下都是不受欢迎的:
Child* c = new Child;
Base* b = c;
c->Foo(); // doesn't work, Child::Foo() is private
b->Foo(); // works, calls Child::Foo()
基本上,你在表达式中引用的声明指示了访问模式 - 但是虚函数破坏了作为另一个函数的函数,然后实际上可以调用命名函数。
答案 1 :(得分:15)
是的,更改派生类中的访问模式是合法的。
类似表单,但不同意图使用Non-Virtual Interface成语。一些基本原理给出了here:
重点是存在虚拟功能以允许定制;除非它们还需要直接从派生类的代码中调用,否则不需要将它们变成私有的。
至于为什么你实际上会在public
基础上创建private
但private
在没有protected
或{{1}}的情况下继承而已超出我的范围。
答案 2 :(得分:5)
这是完全合法的C ++。您只是在Child类中定义一个新方法。
现在它做了你想做的事,这是另一个问题。 我认为访问模式不是方法签名的一部分,这意味着调用Base的Foo虚方法最终会调用Child的Foo方法。
所以这里的结论是:它是合法的c ++,它的工作方式与你期望的一样。
我没有考虑行child.Foo();
无法正常工作,因为毫无疑问它正在尝试访问Child的私有Foo()方法。
答案 3 :(得分:4)
似乎编译并调用正确的方法。
请记住,访问说明符可以帮助纪律严明的程序员,而不是阻止所有尝试不惜一切代价绕过它。
在这种特殊情况下,Child没有业务将被覆盖的虚拟函数设为私有:它是不是应该实现Base的公共接口,所以“is-a”关系成立? (如果你没有使用公共继承,这意味着“孩子是一个基础”,你的伎俩将不起作用。)