2013-04-12 4 views
3

Я знаю, что этот код не будет работать, и я также знаю, почему, но есть ли альтернатива?Участник виртуального базового класса

class A 
{ 
public: 
    A(void){} 
    virtual ~A(void){} 
protected: 
    A* parent; 
    int a; 
}; 

class B : public virtual A 
{ 
public: 
    B(void){} 
    virtual ~B(void){} 
protected: 
    void f(){ ((B*)parent)->a; } 
}; 

Это не представляется возможным бросить parent к B*, поскольку A является виртуальным базовым классом. Не литье parent также дает ошибку. Надеюсь, мне не нужно публиковать всех участников. У кого-то есть идея, как получить доступ к A :: a?

Редактировать

Использование друзей не работает, так как классы, полученные из B не имеют доступа к A::a.

+0

Без броска это, вероятно, не получается, потому что вы пытаетесь получить доступ к защищенному полю A в B. Вы можете просто добавить публичный приемник и удалить бросок в B *. –

ответ

0
class B : public virtual A 
{ 
public: 
    B(void){} 
    virtual ~B(void){} 
protected: 
    void f(){ this->a; } 
}; 

Вы можете получить доступ к защищенному члену из родительского класса (материнский класс B).

+0

Но он хочет получить доступ к 'a' переменной 'parent', а не базовому классу. –

+0

OP хочет получить доступ к 'a' из другого объекта. –

+0

Если B не имеет члена 'int a',' this-> a' из метода B возвращает родительский элемент a, поэтому A :: a. Если он хочет получить член 'a' от другого объекта (не типа B или типа A), он должен реализовать публичный метод получения в классе A. Или я не понял его вопроса – Marcassin

1

Ответ на мой комментарий выше, потому что я тестировал его, и он компилируется отлично.

без броска, он, вероятно, не удается, потому что вы пытаетесь получить доступ к защищенному поле A в B. Можно просто добавить публичный геттер и удалить бросок к B *

class A 
{ 
public: 
    A(void){} 
    virtual ~A(void){} 
    int getA() { return a; } 
protected: 
    A* parent; 
    int a; 
}; 

class B : public virtual A 
{ 
public: 
    B(void){} 
    virtual ~B(void){} 
protected: 
    void f(){ (parent)->getA(); } 
}; 
+0

1+, я планировал использовать это как последний ресурс. Хотя он работает, он дает пользователям доступ к переменным, которые они не могут изменить. – JMRC

+0

@JMRC: вы можете перенаправить объявление класса B и сделать его «другом» класса A. Он не будет выставлять переменные другим классам, но частные члены из А будет доступна Б. – Greg

2

Некоторые варианты:

  1. Сделать a общественного
  2. Создание/геттер общественности сеттера для a -
  3. марка B друг класса А (или просто функция f())

3-й вариант его работы лучше, чем другие 2, если вы хотите разрешить только A (или функция), чтобы иметь доступ к членам от A. С другой стороны, с другими 2 вариантами вы можете сделать только этого участника публичным (но он будет общедоступным для всех)

+0

1+ Использование класса friend работает в этом примере, но если вы извлекаете классы из B, вы должны добавить каждый класс (в моей программе очень много) в качестве друга. – JMRC

+0

@JMRC вы можете создать еще один класс 'C', который наследуется от A и является другом' A' и имеет общедоступный getter/setter для A :: a(). Затем наследуйте от C, где вы хотите получить доступ к этим членам. Таким образом, класс B станет «классом B: public virtual A, public C». Таким образом, только C должен быть знаком с A, и вы можете контролировать, какие классы имеют доступ к setter/getter (но это ** может ** запутаться) – msam

0

Я думаю, что проблема здесь заключается не столько в том, как получить доступ к элементу a, но зачем вам нужен дерева бриллиантов в вашем дизайне. Сначала вы должны отступить и оценить, действительно ли вам нужно дерево бриллиантов.

Если вы сделать, то вместо ваш текущий подход, обеспечивает A с хорошим соответствующим (общественным) абстрактным интерфейсом, который может быть вызван из B. Когда вы начинаете напрямую обращаться к родительским защищенным атрибутам, вы плотно соединяете компоненты, что позволяет легко разбить ваш код и сделать его очень сложным для изменения дизайна позже в течение срока действия приложения.

+0

У меня есть класс 'UI', который имеет класс' container' и 'content'. Класс, такой как HTML 'div', может находиться внутри другого' div' и может содержать 'div'. Необходимо использовать виртуальный базовый класс. – JMRC

1

Это работает:

class A { 
public: 
    A(void){} 
    virtual ~A(void){} 
protected: 
    A* parent; 
    int a; 
    int parent_a(){ return parent->a;} 
}; 

class B : public virtual A 
{ 
public: 
    B(void){} 
    virtual ~B(void){} 
protected: 
    void f(){ A::parent_a(); } 
}; 

Обратите внимание, что:

  • a не получить воздействие внешнего мира,
  • извлеченного a необходимо правильный, поскольку B наследует практически от A, поэтому успешный динамический бросок parent до B перед тем, как получить его a fie ld должен возвращать то же самое, что и предлагаемое выше решение.

Почему выполняет эту работу?

Потому что класс является неявным другом самого себя.

+0

1+ Да, это действительно работает. Я пытался его реализовать, но из-за количества свойств (например, «A :: a') и указателей (например,« A :: parent ») количество функций стало огромным. Я буду помнить об этом, потому что ничего более компактного не появляется. Благодарю. – JMRC

1

Это то, что dynamic_cast для. Если вы не хотите, чтобы перестроить свой код, просто замените бросок C-стиле с dynamic_cast:

void f() { dynamic_cast<B*>(parent)->a; } 

Для этого, чтобы правильно работать, A должны иметь по крайней мере одну виртуальную функцию (как это делает). Кроме того, литой будет вызывать нулевой указатель, если parent не указывает на объект типа B.

+0

B не является полным типом A, поэтому dynamic_cast всегда возвращает NULL. – JMRC

+0

Извините, я не знаю, что вы имеете в виду. Если вы говорите компилятору, что «родитель» фактически указывает на объект типа «B», то «B» лучше получить из «A». Если вы попали в беспорядок, потому что «B» еще не определен, это совершенно другая проблема, которую вы должны решить, реорганизовывая свой код. –

+0

Я имел в виду, что dynamic_cast от 'B' до' A' будет работать, но dynamic_cast от 'A' до' B' вернет NULL. Upcasting будет работать, downcasting не будет. – JMRC

0

Спасибо всем, некоторые полезные возможности были упомянуты, как использование друзей и создание дополнительных функций для каждой переменной. Это может работать в некоторых сценариях, но не в моем случае. Добавление каждого производного класса в качестве друга имеет проблему, заключающуюся в том, что он делает библиотеку менее удобной для пользователей, когда они хотят добавить класс. Добавление функций для каждой переменной добавит более ста дополнительных функций для некоторых классов.

То, что я сделал в качестве своего окончательного решения, заключалось в создании копии вместо трансляции. Хотя он будет работать немного медленнее, код останется чистым. Чтобы противостоять проблеме скорости. Доступ к локальным переменным намного быстрее, чем доступ к глобальным переменным.

Вот как это выглядит сейчас:

class A 
{ 
public: 
    A* parent; 
    virtual ~A(void){} 
    virtual A & operator = (const A & x) 
    { 
     a = x.a; 
     parent = x.parent; 
     return *this; 
    } 
protected: 
    int a; 
}; 

class B : public virtual A 
{ 
public: 
    B(void){} 
    virtual ~B(void){} 
    using A::operator =; 
    virtual B & operator = (const B & x) 
    { 
     A::operator = (x); 
     return *this; 
    } 
    void f(void) 
    { 
     B p; 
     p = *parent; 
     int x = p.a;//Allowed. 
    } 
}; 

Если у вас есть лучшее решение, пожалуйста, бесплатный почтовый его.

Смежные вопросы