30
#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() 是私有的,当我尝试在类外访问它时,编译器不应该产生错误吗?不知何故,它似​​乎不正确。

4

3 回答 3

33

add()只是 private in derived,但你拥有的静态类型是 - 因此applybase*的访问限制。 通常,您甚至无法在编译时知道指针的动态类型是什么,例如,它可能会根据用户输入而改变。base
base

这是根据C++03 §11.6

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

于 2010-08-31T16:11:29.933 回答
12

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

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

于 2011-05-11T09:21:13.980 回答
5

为 Georg 的回答添加一点内容:

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

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

于 2010-08-31T16:28:12.443 回答