1
votes

I have abstract class A, from which I inherit a number of classes. In the derived classes I am trying to access protected function in A trough A pointer. But I get a compiler error.

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();
}

The compiler error says : cannot access protected member A::f declared in class A.

If I declare mAPtr to be D*, instead A* everything compiles. And I don't understand why is this.

3
Your testcase is broken. You have a commented } and you forgot semicolons after your class definitions. Show us an actual testcase please; show us the one you used when you tested/debugged with codepad or ideone. (*ahem*)Lightness Races in Orbit

3 Answers

6
votes

Relying on private access works on unrelated instances of the same type.

Relying on protected access works on unrelated instances of the same type (and of more derived types).

However, relying on protected access does not work on unrelated instances of a base type.

[n3290: 11.5/1]: When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11. Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

So D or something derived from D, but not A.

It's an oft-questioned cute oddity about C++ that nonetheless is designed to try to avoid pitfalls. After all, you don't know what type *mAPtr really has.

2
votes

A class containing a protected section means that this class allows derived classes to manipulate their base class in any way they choose (as far as the protected interface allows).

Class D objects can manipulate their own A part. In doing say they probably want to maintain some invariants.

Suppose there is (or will be in the future!) another class E, also inherited from A. Class E objects also can manipulate their own A part, and they may be enforcing different invariants.

Now, if a class D object was allowed to manipulate the A part of any object, it can't ensure the invariants. A D object may do something to the A part of an E object that breaks that E object. That's why it is not allowed.


But if you really want to, perhaps a way to call A::f, without exposing it to everybody, would be via a friend function.

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;
};

This relies on users being disciplined enough to stay out of namespaces whose name clearly indicates that it contains implementation details.

-2
votes

You forgot using ; after class declaration:

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
};

Besides, you don't need to store base class pointer.