为什么我可以通过指向派生对象的基类指针访问派生私有成员函数?

发布于 2024-09-17 10:11:17 字数 480 浏览 12 评论 0原文

#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

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

#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;
}

Output is bye

I dont have a problem with how this is implemented. I understand you use vtables and the vtable of derived contains the address of the new add() function. But add() is private shouldn't compiler generate an error when I try to access it outside the class? Somehow it doesn't seem right.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

当梦初醒 2024-09-24 10:11:17

add() 仅在派生 中是私有的,但您拥有的静态类型base* - 因此访问base 的限制适用。
一般来说,您甚至无法在编译时知道指向base的指针的动态类型是什么,它可能会根据用户输入而改变。

这是根据C++03 §11.6

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

add() is only private in derived, but the static type you have is base* - thus the access restrictions of base apply.
In general you can't even know at compile time what the dynamic type of a pointer to base will be, it could e.g. change based on user input.

This is per C++03 §11.6:

The access rules (clause 11) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it.
[...] Access is checked at the call point using the type of the expression used to denote the object for which the member function is called [...]. The access of the member function in the class in which it was defined [...] is in general not known.

骄傲 2024-09-24 10:11:17

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

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

Access modifiers, such as public, private and protected are only enforced during compilation. When you call the function through a pointer to the base class, the compiler doesn't know that the pointer points to an instance of the derived class. According to the rules the compiler can infer from this expression, this call is valid.

It is usually a semantic error to reduce the visibility of a member in a derived class. Modern programming languages such as Java and C# refuse to compile such code, because a member that is visible in the base class is always accessible in the derived class through a base pointer.

岁月苍老的讽刺 2024-09-24 10:11:17

对 Georg 的答案补充一点:

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

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

To add a little to Georg's answer:

Remember that the compiler has no control over and cannot guarantee anything about derived classes. For example, I could ship my type in a library and derive from it in an entirely new program. How is the library compiler supposed to know that derived might have a different access specifier? The derived type didn't exist when the library was compiled.

In order to support this, the compiler would have to know access specifiers at runtime and throw an exception if you attempted to access a private member.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文