Друг вызывает виртуальный приватный метод - что должно произойти
У меня была ситуация, когда я хотел, чтобы класс друга вызывал закрытый метод, затем я хотел сделать этот метод виртуальным, чтобы вместо него вызывался метод класса - тогда я, конечно, понял, что дружба не наследуется. Таким образом, у нас есть ситуация, когда виртуальный метод означает, что метод производного класса должен быть вызван, но этот метод является частным, поэтому не может быть вызван. Который имеет приоритет?
Я проверил это на MSVC++ 2008 следующим образом
#include<iostream>
class Loner;
class Base
{
friend Loner;
private:
virtual void test(){std::cout << "Base" << std::endl;}
};
class Derived : public Base
{
private:
virtual void test(){std::cout << "Derived" << std::endl;}
};
class Loner
{
public:
void test(Base *base){base->test();}
};
int main()
{
Loner loner;
Derived derived;
loner.test(&derived);
}
Выход был:
Derived
Таким образом, кажется, что виртуальная функция "побеждает" и дает частному члену доступ к не-другу - почти наследственное наследство!
Мой вопрос, кто-нибудь знает, если это правильное поведение? Когда я наконец доберусь до обновления версии своего компилятора или попробую GCC, это поведение может измениться?
ура
Фил
2 ответа
§ 11,5/1-2 ([class.access.virt]):
Правила доступа (пункт 11) для виртуальной функции определяются ее объявлением и не зависят от правил для функции, которая впоследствии переопределяет ее.
Доступ проверяется в точке вызова с использованием типа выражения, используемого для обозначения объекта, для которого вызывается функция-член...
Так что вы хороши для обновления. (В действующем стандарте есть пример, но я исключил его из цитаты.)
По сути, здесь происходит то, что Base
Виртуальная функция отправляет ее переопределения. Так что даже если вы не можете позвонить Derived
переопределить статически (Derived::member
), вы все еще можете получить Base::member
позвонить вам, если у вас есть доступ к Base::member
,
Вызов выполняется через указатель объекта со статическим типом Base*
- это тип, с которым производятся проверки доступа. Эти проверки доступа происходят во время компиляции. поскольку Loner
друг Base
, компилятор в порядке с разрешением вызова base->test()
,
Тем не менее, динамический тип объекта, который base
указатель указывает на Derived
, Во время выполнения дополнительных проверок доступа нет - выполняется вызов динамического типа, который отправляется через обычный механизм виртуального вызова.