Доступ одного класса к protected полям другого класса с общим предком
Я делаю некую имитацию UI. Проблема в том, что при добавлении элемента в layout, он подгоняет позицию элемента под позицию других элементов и следовательно мне нужен доступ к этой переменной(_position). Однако компилятор не разрешает это сделать. Так как все элементы имеют одинакового предка, то по идее должен быть способ как-то обмануть компилятор. Изменять область видимости на public не очень хочется, так как нарушается инкапсуляция.
class UIObject{
protected:
Rect _position;
...
}
class Text : public UIObject
{
...
}
class Layout : public UIObject
{
...
public:
void add_object(UIObject* object){
добавление в коллекцию
...
изменение _position у object
...
}
}
Ответы (3 шт):
В данном случаи объект Layout не является потомком объекта object, поэтому его функция_член не может пользоваться защищенным членом object. Но его функция_член может пользоваться защищенным и закрытым членом своего класса, поэтому, если приводить указатель к указателю на Layout, то можно получать доступ:
void add_object(UIObject* object) {
static_cast<Layout*>(object)->_position = /*...*/;
}
Обмануть компилятор можно через указатель-на-член. Но лучше найти какое-то нормальное решение, без подобных выкрутасов.
#include <iostream>
struct A
{
protected:
int x = 42;
};
struct B : A
{
void foo(A *a)
{
// Не работает:
// std::cout << a->x << '\n';
// Вместо `auto` можно использовать `int A::*`.
auto member = &B::x;
std::cout << a->*member << '\n';
}
};
Почитав немного сайтов на данную тематику, я понял, что такой вопрос встречается довольно часто. Поэтому я решил немного резюмировать всю информацию. Доступ к protected членам запрещён документацией. Но это можно обойти, используя "pointers to members". Большое спасибо HolyBlackCat за оказанную помощь. Ниже я оставлю свой код(здесь так же есть использование и виртуальных функций, получилось добраться только так (компилятор msvc)).
class UIObject {
protected:
Rectangle _position;
protected:
virtual void update_position() {}
}
class Layout :
public UIObject
{
protected:
std::vector<std::unique_ptr<UIObject>> _objects;
}
class HorizontalLayout :
public Layout
{
private:
virtual void update_position() override {
bool first_object_flag = true;
decltype(auto) object_position = &HorizontalLayout::_position;
_position.width = 0;
for (auto&& object : _objects) {
if (first_object_flag) {
(*object.*object_position).x = _position.x;
_position.width += (*object.*object_position).width;
first_object_flag = false;
}
else {
(*object.*object_position).x = _position.x + _position.width + 1;
_position.width += ((*object.*object_position).width + 1);
}
(*object.*object_position).y = (_position.y * 2 + _position.height - 1) / 2 - object->height() / 2;
decltype(auto) object_update_positiion = &HorizontalLayout::UIObject::update_position;
((*object).*object_update_positiion)();
}
public:
template<class _UIObject, class... Args>
_UIObject& add_object(Args&&... args) {
_objects.emplace_back(std::make_unique<_UIObject>(std::forward<Args>(args)...));
decltype(auto) object = *_objects.back();
decltype(auto) object_position = &HorizontalLayout::_position;
if (_objects.size() == 1) {
(object.*object_position).x = _position.x;
_position.width += (object.*object_position).width;
}
else {
(object.*object_position).x = _position.x + _position.width + 1;
_position.width += ((object.*object_position).width + 1);
}
if(object.height() > _position.height){
_position.height = object.height();
this->update_position();
}
(object.*object_position).y = (_position.y*2 + _position.height - 1) / 2 - object.height() / 2;
return static_cast<_UIObject&>(object);
}
};