Почему я могу получить доступ к производной закрытой функции-члену через указатель базового класса на производный объект? - PullRequest
30 голосов
/ 31 августа 2010
#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, а vtable производного содержит адрес новой функции add (). Но add () является приватным, не должен ли компилятор выдавать ошибку, когда я пытаюсь получить к нему доступ вне класса? Почему-то это не так.

Ответы [ 3 ]

33 голосов
/ 31 августа 2010

add() является приватным только в derived, но статический тип , который у вас есть, base* - таким образом, применяются ограничения доступа base.
В общем, вы даже не можете знать во время компиляции, каким будет динамический тип указателя на base, это может быть, например, изменение в зависимости от ввода пользователя.

Это для C ++ 03 §11,6 :

Правила доступа (пункт 11) для виртуальной функции определяются ее объявлением и не зависят от правил для функции, которая впоследствии переопределяет ее.
[...] Доступ проверяется в точке вызова с использованием типа выражения, используемого для обозначения объекта, для которого вызывается функция-член [...]. Доступ к функции-члену в классе, в котором она была определена [...], вообще неизвестен.

12 голосов
/ 11 мая 2011

Модификаторы доступа, такие как public, private и protected, применяются только во время компиляции.Когда вы вызываете функцию через указатель на базовый класс, компилятор не знает, что указатель указывает на экземпляр производного класса.Согласно правилам, которые компилятор может вывести из этого выражения, этот вызов действителен.

Обычно семантическая ошибка уменьшает видимость члена в производном классе.Современные языки программирования, такие как Java и C #, отказываются компилировать такой код, потому что член, видимый в базовом классе, всегда доступен в производном классе через базовый указатель.

5 голосов
/ 31 августа 2010

Чтобы добавить немного ответа Георга:

Помните, что компилятор не контролирует и не может гарантировать что-либо в отношении производных классов. Например, я могу отправить свой тип в библиотеку и извлечь из него совершенно новую программу. Как компилятор библиотеки должен знать, что производный может иметь другой спецификатор доступа? Производный тип не существовал на момент компиляции библиотеки.

Для поддержки этого компилятор должен знать спецификаторы доступа во время выполнения и выдавать исключение, если вы пытались получить доступ к закрытому члену.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...