2010-08-31 4 views
29
#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() является частным, если компилятор не генерирует ошибку при попытке получить к ней доступ за пределами класса? Почему-то это не кажется правильным.

+4

Спецификаторы переопределения и доступа - это ортогональные концепции. – sbi

+0

vtables - это деталь реализации. –

ответ

32

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

Это на C++ 03 §11.6:

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

+0

+1 - Спасибо за пояснительный комментарий к моему ответу. –

+0

@Georg Fritzsche Не могли бы вы дать мне ссылку C++ 03, где вы скопировали эту ссылку. Я googled, но не нашел, хочу изучить C++ – 2014-05-25 17:46:41

+0

@Jai: ["Где найти текущие стандартные документы C или C++?"] (Http://stackoverflow.com/questions/81656/where-do-i-find -the-current-c-or-c-standard-documents) –

5

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

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

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

10

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

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

+1

+1 Лучший ответ. –