Как получить доступ к функции защищенного базового класса из производного класса через базовый класс ptr

У меня есть абстрактный класс A, от которого я унаследовал несколько классов. В производных классах я пытаюсь получить доступ к защищенной функции в указателе A через A. Но я получаю ошибку компилятора.

class A
{
   protected:
        virtual  void f()=0;
};

class D : public A
{
    public:
         D(A* aa) :mAPtr(aa){}
         void g();

    protected:
         virtual void f();

    private:
      A* mAPtr; // ptr shows to some derived class instance
};

void D::f(){  }


void D::g()
{
   mAPtr->f();
}

Ошибка компилятора говорит: не удается получить доступ к защищенному члену A :: f, объявленному в классе A.

Если я объявляю mAPtr как D *, вместо A * все компилируется. И я не понимаю, почему это так.


person user152508    schedule 10.10.2011    source источник
comment
Ваш тестовый пример не работает. У вас есть закомментированный }, и вы забыли точку с запятой после определений вашего класса. Покажите нам реальный тестовый пример, пожалуйста; покажите нам тот, который вы использовали при тестировании / отладке с помощью codepad или ideone. (* кхм *)   -  person Lightness Races in Orbit    schedule 10.10.2011
comment
возможный дубликат Защищенные данные в родительском классе недоступны в дочернем класс?   -  person Lightness Races in Orbit    schedule 10.10.2011


Ответы (3)


Использование private доступа работает с несвязанными экземплярами одного и того же типа.

Использование protected доступа работает с несвязанными экземплярами одного и того же типа (и более производных типов).

Однако использование protected доступа не работает с несвязанными экземплярами базового типа.

[n3290: 11.5/1]: Когда друг или функция-член производного класса ссылается на защищенную нестатическую функцию-член или защищенный нестатический член данных базового класса, проверка доступа применяется в дополнение к тем, которые описаны ранее в пункте 11. За исключением случаев формирования указателя на член (5.3.1), доступ должен осуществляться через указатель, ссылку или объект самого производного класса (или любой класс, производный от этого класса) (5.2.5). Если доступ заключается в формировании указателя на член, спецификатор вложенного имени должен называть производный класс (или любой класс, производный от этого класса).

Итак, D или что-то производное от D, но не A.

Это часто обсуждаемая милая странность C ++, которая, тем не менее, призвана избегать ловушек. В конце концов, вы не знаете, какой у *mAPtr на самом деле тип.

person Lightness Races in Orbit    schedule 10.10.2011

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

Объекты класса D могут управлять своей собственной частью A. При этом говорят, что они, вероятно, хотят сохранить некоторые инварианты.

Предположим, существует (или будет в будущем!) Другой класс E, также унаследованный от A. Объекты класса E также могут манипулировать своей собственной частью A, и они могут применять разные инварианты.

Теперь, если объекту класса D было разрешено манипулировать частью A любого объекта, он не может гарантировать инварианты. Объект D может делать что-то с частью A объекта E, что нарушает этот объект E. Вот почему это запрещено.


Но если вы действительно хотите, возможно, способ вызвать A :: f, не раскрывая его всем, был бы через функцию друга.

class A;

namespace detail
{
   void call_f(A*);
}

class A
{
   friend void detail::call_f(A*);
private:
   virtual void f() = 0;
};

namespace detail
{
   void call_f(A* a) { a->f(); }
}

class D: public A
{
public: 
    void g() { detail::call_f(mAPtr); }
private: 
    void f() {}
    A* mAPtr;
};

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

person visitor    schedule 10.10.2011

Вы забыли использовать ; после объявления класса:

class A
{
   protected:
       virtual  void f()=0;
};

class D : public A
{
    public:
        void g();

    protected:
        void f();

    private:
       A* mAPtr; // ptr shows to some derived class instance
};

Кроме того, вам не нужно хранить указатель базового класса.

person Tio Pepe    schedule 10.10.2011
comment
Помимо опечаток в тестовом примере, вы упустили проблему. А откуда вы знаете, что ему не нужно хранить A*? Иначе зачем он это делал? - person Lightness Races in Orbit; 10.10.2011
comment
Это не отвечает на вопрос. - person emem; 29.12.2014