33

考虑以下代码段:

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++ 吗?“This”正在改变派生类中虚函数的访问模式。

4

4 回答 4

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()

基本上,您在表达式中引用的声明决定了访问模式 - 但虚函数破坏了这一点,作为另一个函数,那么命名的函数实际上可能被调用。

于 2010-01-26T17:57:10.260 回答
20

是的,在派生类中更改访问模式是合法的。

这在形式上与Non-Virtual Interface习惯用法相似,但意图不同。这里给出了一些理由:

关键是存在虚拟功能以允许定制;除非还需要从派生类的代码中直接调用它们,否则除了私有之外,没有必要将它们设为任何东西。

至于为什么你实际上会public在基础上制作一些东西,但在没有或继承的private情况下派生,这超出了我的理解。privateprotected

于 2010-01-26T17:40:50.890 回答
5

它是完全合法的 C++。您只是在 Child 类中定义一个新方法。

现在它会做你想让它做的事情,这是另一个问题。我相信访问模式不是方法签名的一部分,这意味着调用 Base 的 Foo 虚拟方法最终会调用 Child 的 Foo 方法。

所以这是结论:它是合法的 c++ 并且它按照您期望的方式工作。

我没有考虑child.Foo();无法工作的行,因为毫无疑问它正在尝试访问 Child 的私有 Foo() 方法。

于 2010-01-26T17:27:55.553 回答
4

它似乎编译并调用了正确的方法。

请记住,访问说明符的存在是为了帮助有纪律的程序员,而不是阻止所有试图不惜一切代价规避它的尝试。

在这种特殊情况下,Child 没有必要将重写的虚函数设为私有:它不应该实现 Base 的公共接口,所以“is-a”关系成立吗?(如果你不使用公共继承,这意味着“孩子是一个基础”,你的把戏就行不通了。)

于 2010-01-26T17:33:00.310 回答