Как получить доступ к функции защищенного базового класса, от производного класса до базового класса ptr - PullRequest
1 голос
/ 10 октября 2011

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

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 * все компилируется.И я не понимаю, почему это так.

Ответы [ 3 ]

6 голосов
/ 10 октября 2011

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

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

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

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

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

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

2 голосов
/ 10 октября 2011

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

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

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

Теперь, если объекту класса D было разрешено манипулировать частью A любого объекта, он не может обеспечить инварианты.Объект AD может что-то делать с частью 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;
};

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

0 голосов
/ 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
};

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

...