Частные методы внезапно недоступны при цепочке
У меня есть простая иерархия классов с базовым классом и производным классом. База имеет два защищенных члена, вызываемых производным классом. Исходя из недавних опытов С#, я подумал, что было бы неплохо сделать интерфейс немного более свободным и позволить цепочку вызовов методов, поэтому вместо вызова this->A()
, затем this->B()
вы можете позвонить this->A()->B()
. Однако следующий код не будет компилироваться:
#include <iostream>
class Base
{
protected:
Base* A()
{
std::cout << "A called." << std::endl;
return this;
}
Base* B()
{
std::cout << "B called." << std::endl;
return this;
}
};
class Derived : public Base
{
public:
void Test()
{
// Base::A and Base::B are private here.
this->A() // This works fine
->B(); // Suddenly I cannot access my own private method?
}
};
int main()
{
Derived d;
d.Test();
return 0;
}
Это приводит к следующей ошибке компилятора:
main.cpp: In member function 'void Derived::Test()':
main.cpp:12:15: error: 'Base* Base::B()' is protected
Base* B()
^
main.cpp:26:21: error: within this context
->B(); // Suddenly I cannot access my own private method?
^
Я также попытался сделать методы базового класса виртуальными, но это не помогло.
Мой С++ достаточно ржавый, и я не могу понять, что здесь происходит, поэтому помощь будет высоко оценена. Также мне было интересно, если это плохая идея, потому что C++ != C#
и С++ - люди не привыкли к таким свободным интерфейсам.
Ответы
Ответ 1
Защищенный член класса доступен из производного класса только через этот производный класс, то есть через объект или ссылку или указатель на этот производный класс.
Возвращаемым типом A()
является Base*
, который не является производным классом, поэтому вы не можете получить доступ к его защищенным членам. Компилятор не отслеживает, что он действительно относится к одному и тому же объекту.
Ответ 2
Да, вы не можете вызвать защищенные методы класса Base
из Base *
. Вы можете думать, что защищенные методы являются частными с той разницей, что они становятся частными из производного класса.
Ответ 3
Это правильное поведение, вы не можете вызвать защищенную функцию для другого класса, который вы можете выполнять только через производный класс, потому что, когда вы вызываете this->A()
, он возвращает Base *, который является другим классом. Причина в том, что, если вы сделали что-то вроде
class Derived : public Base
{
public:
void Test()
{
baseInstance->B(); // this shouldn't be possible. If the case was you can call it through a pointer or an object this would be possible.
}
Base* baseInstance;
};
Также полезно отметить, что производный и базовый this
могут не иметь одного и того же адреса, он может иметь другой адрес. Когда вы на самом деле бросаете Base*
в Derived*
, компилятор будет обрабатывать разницу в адресе, что делает возможным, почему он работает, если это было сделано так: static_cast<Derived*>(this->A())->B();
Ответ 4
Чтобы добавить к Себастьяну, это можно решить, но не красиво, по:
static_cast<Derived*>(this->A())->B();
Ответ 5
Вы можете обратиться к стандарту, чтобы получить ответ на вашу проблему.
11.2 Доступность базовых классов и членов базового класса [class.access.base]
Базовый класс B из N доступен в R, если
— an invented public member of B would be a public member of N, or
— R occurs in a member or friend of class N, and an invented public member of B would be
a private or
protected member of N, or
— R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P, or
— there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R
Если вы ссылаетесь на элемент с помощью указателя Base, ни одно из указанных выше статусов.