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

Posted

技术标签:

【中文标题】为啥我可以通过指向派生对象的基类指针访问派生私有成员函数?【英文标题】: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 是一个实现细节。 【参考方案1】:

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

这是根据 C++03 §11.6

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

【讨论】:

@Georg Fritzsche 你能不能给我你复制这个链接的c++03链接。 google了一下没找到,想学c++ @Jai: "Where do I find the current C or C++ standard documents?"【参考方案2】:

为 Georg 的回答补充一点:

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

以上是关于为啥我可以通过指向派生对象的基类指针访问派生私有成员函数?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中将派生类对象分配和访问到基类“指向指针”对象

指向派生对象的基类指针的 C++ 排序容器

关于C++基类、派生类的引用和指针

朋友类对象可以在其成员函数中访问派生类对象上的基类私有成员吗?

为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?

如果继承类型受到保护,我可以使基类的指针指向派生对象吗? [复制]