【问题标题】:Why can I access a derived private member function via a base class pointer to a derived object?为什么我可以通过指向派生对象的基类指针访问派生私有成员函数?
【发布时间】:2011-04-06 08:56:21
【问题描述】:
#include<iostream>

using namespace std;
class base
{
public:
    virtual void add() {
        cout << "hi";
    }
};

class derived : public base
{
private:
    void add() {
        cout << "bye";
    }
};

int main()
{
    base *ptr;
    ptr = new derived;
    ptr->add();
    return 0;
}

输出为bye

我对如何实现没有问题。我了解您使用 vtables,并且 derived 的 vtable 包含新的 add() 函数的地址。但是 add() 是私有的,当我尝试在类外访问它时,编译器不应该产生错误吗?不知怎的,这似乎不对。

【问题讨论】:

  • 覆盖和访问说明符是正交的概念。
  • vtables 是一个实现细节。

标签: c++ inheritance polymorphism access-modifiers


【解决方案1】:

add() 仅在derived 中是私有的,但您拥有的静态类型base* - 因此适用base 的访问限制。
通常,您甚至无法在编译时知道指向base 的指针的动态类型是什么,例如根据用户输入进行更改。

这是根据 C++03 §11.6

虚函数的访问规则(第 11 条)由其声明决定,不受稍后覆盖它的函数规则的影响。
[...] 在调用点使用表达式类型检查访问权限,该表达式用于表示调用成员函数的对象 [...]。定义它的类中的成员函数的访问 [...] 通常是未知的。

【讨论】:

【解决方案2】:

为 Georg 的回答补充一点:

请记住,编译器无法控制也不能保证派生类的任何事情。例如,我可以将我的类型发送到一个库中,并在一个全新的程序中从它派生。库编译器应该如何知道派生可能具有不同的访问说明符?编译库时派生类型不存在。

为了支持这一点,编译器必须在运行时知道访问说明符,并在您尝试访问私有成员时抛出异常。

【讨论】:

    【解决方案3】:

    访问修饰符,例如 publicprivateprotected 仅在编译期间强制执行。当您通过指向基类的指针调用函数时,编译器不知道该指针指向派生类的实例。根据编译器从这个表达式中可以推断出的规则,这个调用是有效的。

    降低派生类中成员的可见性通常是语义错误。现代编程语言(如 Java 和 C#)拒绝编译此类代码,因为在基类中可见的成员始终可以通过基指针在派生类中访问。

    【讨论】:

      猜你喜欢
      • 2011-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-05
      • 2014-12-05
      • 1970-01-01
      • 2017-07-18
      • 1970-01-01
      相关资源
      最近更新 更多