У меня есть иерархия следующим образом:
class Element{ public : virtual void Accept(Visitor&) = 0
protected : Element(); int num;
};
class ElementA : public Element{
public : ElementA();
void Accept(Visitor& v) {v.Visit(this};}
};
class ElementB : public Element{
public : ElementB();
void Accept(Visitor& v) {v.Visit(this};}class Visitor{
public: void Visit(ElementA*);
void Visit(ElementB*);
};
РЕДАКТИРОВАТЬ:
Требуется добавить метод int getNum () в иерархию, которая предоставит значение num. Однако для этого потребуется снова скомпилировать всю иерархию, и нам не разрешено это делать. Таким образом, мы должны изменить дизайн иерархии таким образом, чтобы перекомпиляция иерархии не требовалась.
То, что вы хотите сделать, не возможно в чисто продуманном виде. Я понятия не имею, почему полная перекомпиляция этой иерархии была бы такой проблемой, но есть решение, которое технически возможно без использования UB-хаков, таких как reinterpret_cast
взлом защиты доступа и других хаков.
int Visitor::getNum(Element* pe)
{
//define a pick-pocket... ;)
struct ElementNumAccessor : private Element
{
ElementNumAccessor(Element const& e) : Element(e) {}
int getNum() { return num; }
void Accept(Visitor&); //has to be declared, but needs not be defined
};
//...and let him work:
ElementNumAccessor ea(*pe);
return ea.getNum();
}
Эта душа в действии: http://ideone.com/e1chSX
Это использует тот факт, что защищенный доступ является транзитивным, но за счет копии каждого элемента, из которого вы хотите получить число. Я сделал структуру локальным классом функции, так что никто не может даже использовать ее для каких-либо других целей.
Но имейте в виду, что эта техника — взлом, эксплойт языковой функции, которая не предназначена для использования таким образом.
Мой совет: если ваша иерархия настолько запутана в программе, что ее изменение приводит к ужасу перекомпиляции, тогда самое время реорганизовать вашу программу, чтобы уменьшить зависимости времени компиляции, а затем внести необходимые изменения.
Одна возможность состоит в том, чтобы объявить GetNumVisitor
как друг Element class
и получить доступ к num
переменная-член напрямую. Затем добавьте метод в GetNumVisitor
вернуть значение.
class Element {
...
friend GetNumVisitor; // declare GetNumVisitor as a friend class
...
};
GetNumVisitor : public Visitor {
private:
int m_numelement;
public :
void visit(Element *E) { m_elementNum = E->num; }
int getNum() const { return m_elementNum;}
};
Вы должны будете назвать это как
ElementA element_a();
ElementB element_b();
GetNumVisitor getnumVisitor();
element_a.accept(getnumVisitor);
int a = getnumVisitor.getNum();
element_b.accept(getNumVisitor);
int b = getnumVisitor.getNum();
...
Не эксперт по шаблону посетителя, но соответствует ли это вашим требованиям?
class Element{ public : virtual void Accept(Visitor&) = 0
protected : Element(); int num;
};
class ElementA : public Element{
public : ElementA();
void Accept(Visitor& v) {v.setNum(this->num); v.Visit(this);}
};
class ElementB : public Element{
public : ElementB();
void Accept(Visitor& v) {v.setNum(this->num); v.Visit(this);}class Visitor{
public:
void Visit(ElementA*);
void Visit(ElementB*);
void setNum(int _num) {num = _num;}
int getNum() {return num;}
private:
int num;
};
Ваш вопрос, как разрешить класс (Visitor
) для прямого доступа к частному члену данных, унаследованному в другой иерархии классов (Element
с).
Быстрый ответ будет: не надо! это сломает всю цель модификаторов видимости.
Однако делать это все еще возможно с помощью (приводит к перекомпиляции иерархии) или с помощью реинтерпретации указателей.friend
Чистое решение будет использовать шаблон адаптера (используя наследование) для дублирования всего Element
s иерархия в другую иерархию заглушки, которая обеспечивает точно такой же интерфейс с делегатами для соответствующих функций, обеспечивает функцию получения для num
и использовать эту новую иерархию с вашим посетителем, который сможет получить доступ num
через геттер.