2016-11-05 9 views
0

Я не продвинутый программист. Предположим, что существует классический алмаз наследования:Невозможно получить доступ к защищенным переменным-членам большинства базового класса через std :: unique_ptr в алмазе

class Base 
class A: virtual public Base 
class B: virtual public Base 
class Last: public A, public B 

Пусть Base имеет переменную m_x, что является общим для обоих A и B, таким образом, что только один из A или B, можно назвать в то время, не и то, и другое (что и нужно). Чтобы обойти эту проблему, это используется:

class Last: public A, public B 
{ 
private: 
    std::unique_ptr<Base> m_p; 
public: 
    Last(int i) 
    { 
     if (i) 
      m_p = std::unique_ptr<Base>(new A()); 
     else 
      m_p = std::unique_ptr<Base>(new B()); 
    } 
}; 

Это прекрасно, но теперь m_p->m_x не может быть доступно больше, потому что он говорит, он защищен, но и A и B вызов m_x в своих конструкторах напрямую, без каких-либо проблем.

Является ли это известным ограничением или это неправильный способ сделать это? Если это неправильно, какие решения есть?


Вот код, основанный на диаграмме нашли here (чуть ниже на странице):

#include <iostream> 
#include <memory> 

class Power 
{ 
protected: 
    double m_x; 
public: 
    Power() {} 
    Power(double x): m_x {x} {} 
    virtual ~Power() = default; 
}; 

class Scanner: virtual public Power 
{ 
public: 
    Scanner() {} 
    Scanner(double x): Power(x) {} // scan document 
}; 

class Printer: virtual public Power 
{ 
public: 
    Printer() {} 
    Printer(double x): Power(x) {} // print document 
}; 

class Copier: public Scanner, public Printer 
{ 
private: 
    std::unique_ptr<Power> m_p; 
public: 
    Copier() {} 
    Copier(double x, int i) 
    { 
     if (i) 
      m_p = std::unique_ptr<Power>(new Scanner(x)); 
     else 
      m_p = std::unique_ptr<Power>(new Printer(x)); 
    } 
    void print() { std::cout << this->Power::m_x << '\n'; } 
}; 

int main(int argc, char *argv[]) 
{ 
    Copier *copier {new Copier(1.618, 0)}; 
    copier->print(); 
    copier = new Copier(3.14, 1); 
    copier->print(); 

    return 0; 
} 

Используя оба this->m_p и this->Power::m_x (в соответствии с ответами и комментариями) компилирует, но выход есть 0.


Чтобы убедиться, что я объяснить это все: не только я совсем новичок, но, учитывая пример выше, oesn't действительно должен оставаться таким образом, если есть другая альтернатива называть Scanner или Printer только один за один раз из внутри Copier. Я не прошу мнения, я понимаю, что это запрещено, но я не буду отвергать их от более опытных пользователей. В конце концов, я учусь.

+3

Я не совсем понимаю, как 'Last' * is-a * A и * is-a * B, но затем * has-a * либо A, либо B ... для меня не имеет смысла. – nvoigt

+2

'm_p' не ваш базовый объект. 'это' есть. –

+0

@CaptainGiraffe Вы имеете в виду, вместо использования 'm_p-> m_x', я должен использовать' this-> m_x'? Я просто заменил 'm_p' на' this', и он компилируется, но, похоже, он не работает. Переменные - 0, все. Не могли бы вы рассказать? –

ответ

2

protected не означает, что вы думаете, что он делает.

Хотя Last является производным от Base, функция-членов Last не имеет доступ к защищенным членам любогоBase объекта - только тем Base объектов, которые являются субом-объектами некоторых Last объекта.

Таким образом, вы можете написать: this->Base::x потому *this это Last объект, но не m_p->x, потому что *m_p имеет статический Base.

Как и другие, я думаю, что это на самом деле XY problem. Наличие объекта, который происходит из двух классов, а затем также имеет указатель на другой объект одного из этих классов, очень странно. Я думаю, вам нужно уточнить, что вы пытаетесь сделать.

+0

Боюсь, я не могу опубликовать код, но надеюсь, что пример из недавно отредактированного вопроса имеет больше смысла. –

+0

Нет, боюсь, нет. Вы только что заменили имена, но я до сих пор не вижу, что вы пытаетесь сделать. Что представляет собой базовый класс «Сила»? –

+0

Я привел пример после ссылки в моем вопросе, я полагаю, вы могли бы подумать об этом как источнике питания и, возможно, контрольном блоке. Таким образом, вы также можете сказать, что 'm_x' - это память (RAM, USB, harddisk и т. Д.), Где документы сохраняются после сканирования, а затем используются для редактирования/печати и т. Д. Это абстрактный пример. Или вы могли бы подумать о фабрике промышленных машин, изготовлении тракторов, бульдозеров и т. Д., Которые все начинают работать, по одному, как называется, на рабочем месте. Что-нибудь в этом направлении. –

3

И виртуальное наследование, и std::unique_ptr - это красные сельди. Проблема сводится к следующему:

class Base 
{ 
protected: 
    int m_x; 
}; 

class Last : public Base 
{ 
public: 
    Last() 
    { 
     Base base; 
     base.m_x = 0; // error 
     m_x = 1; // no error 
    } 
}; 

ошибка что-то вроде error C2248: 'Base::m_x': cannot access protected member declared in class 'Base' или error: 'int Base::m_x' is protected within this context.

Объяснение состоит в том, что protected представляет собой несколько частный случай. Он не работает только на уровне класса, но также находится на уровне объекта. И у вас есть две соответствующие объекты здесь:

  1. В Last объект, который создается с помощью конструктора, то есть один, на который указывает this. Это также объект Base из-за is-a наследование отношений.
  2. Локальный объект с именем base внутри конструктора.

Проблема в том, что в строке base.m_x = 0; вы находитесь в контексте первого объекта, а не второго. Другими словами, вы пытаетесь получить доступ к m_x из base снаружи base. C++ просто не допускает этого.

Очень техническое объяснение можно найти в стандарте C++ в §11.4 [class.protected], более понятном в excellent answer здесь, в разделе Переполнение стека.

+0

Тогда, если они «красные сельди», их можно избежать? Я отредактировал ответ, надеюсь, теперь он станет яснее. –

+1

@aconcernedcitizen: их можно избежать, представив [MCVE] в вопросе. –

+0

@LightnessRacesinOrbit Я отредактировал ответ со вторым примером. В целом, это в основном то, что я хотел бы сделать. Разве это недостаточно хорошо? –

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

  • Нет связанных вопросов^_^