3

I was having a discussion with a coworker about why the following does not compile in Visual Studio 2008:

class base
{
protected:
    virtual void f(){}
};

class a : public base
{
public:
    void fa(base* pb)
    {
        pb->f(); // error C2248: 'base::f' : cannot access protected member declared in class 'base'
    }
};

He thinks this is perfectly reasonable but I think it's a strange restriction given, if I wanted base and all of its derived classes to be a closed system, I still need to make some of base's members public so they can all talk to each other through the shared interface they are all derived from publicly.

Is there some use case I'm not thinking of where allowing access to these protected members could break the nature of protected members?

cppguy
  • 3,611
  • 2
  • 21
  • 36

3 Answers3

2

If the compiler allows such thing, then you can easily break encapsulation. Think about this:

base b;
a foo;
foo.fa(b); // we can now easily access/modify protected elements of `b`

In this case, there is no relation between the derived object foo and the base b, however you can use a derived to access its "guts". This should not be possible (at least imho).

Just doing f() inside a.fa() is OK, since you just modify the base part of a, and not some un-related object.

To be more specific, you can write a "wrapper" which will disable protected for any class:

#include <iostream>

class Base
{
public: // protected in your case, public here so it compiles
    int x{42};
public:
    int getx() {return x;}
};

template<typename T> // wrapper
class DisableProtected: public T
{
public:
    void modify(Base* b)
    {
        b->x = 24;
    }
};

int main()
{
    Base base;
    std::cout << base.getx() << std::endl;
    DisableProtected<Base> foo; 

    foo.modify(&base); // can modify any Base
    std::cout << base.getx() << std::endl;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • I'm no sure that's such a terrible thing. It's certainly less evil IMO than forcing me to make members public that have no business being public. – cppguy Apr 03 '15 at 19:14
  • @cppguy I agree, but overall using your scheme basically make `protected` the same as `public` via the scheme I mentioned. This is probably the reason Stroustrup didn't want to allow it. Otherwise, anyone can write a wrapper with a function taking a pointer to `Base`, then access protected members of any `Base` object via the wrapper. Evil? Not really, but then `protected` looses its meaning. – vsoftco Apr 03 '15 at 19:20
  • 1
    I'm not sure I buy this argument. You're not modifying elements of `b` yourself, you're doing it via a method that logically *should* have the ability to modify those elements. Contrast that with the protection of `private`, you can access private elements of another object with no problem. – Mark Ransom Apr 03 '15 at 19:24
  • @vsoftco Well that would be the distinction between protected and private. Protected is a more permissive hiding of data allowing some access in some cases. It seems to me, if you wanted to prevent the issue you're describing, private would suffice. – cppguy Apr 03 '15 at 19:27
  • @MarkRansom Granted, for methods perhaps it makes sense. But what about member variables? You can just do `p->m = 42`, so you directly modify it in that case. And making a different rule for member vars. would have been probably too much to already a complicated language. – vsoftco Apr 03 '15 at 19:29
  • @cppguy if it's `private`, then the derived classes won't have access to it anymore, so `protected` makes some sense. Anyway, good question, I'd like to see other opinions on the matter. – vsoftco Apr 03 '15 at 19:30
  • My comment applies to member variables as well as methods. Member functions should be trusted to change the state of *any* object of that class, just as they can with `private` variables. – Mark Ransom Apr 03 '15 at 19:36
  • @MarkRansom I'm not sure I understand your comment about private. Replacing protected with private in my example wouldn't compile either – cppguy Apr 03 '15 at 19:40
  • 1
    @cppguy not your example, but a similar example that doesn't involve a derived class. A class method can access its own private members, and also the private members of another object of that class (via pointer or reference). See example http://ideone.com/5pYgE5 – Mark Ransom Apr 03 '15 at 19:45
  • @MarkRansom I see what you mean now... so basically the question is why `Derived` objects don't have full access to `Base` objects... I still think it's because you can then elude the `protected`. When you access `Base` via `Base`, then everything is OK, that's how the class was designed by the one who implemented `Base`. However, if OP code is accepted, just deriving from `Base` will then allow you to access `protected` members of any `Base`. That's why imo the code is rejected. In some sense, one can build a new idiom: accessing `protected` members via `Derived` wrappers. – vsoftco Apr 03 '15 at 19:52
2

Near-duplicates:

Is there some use case I'm not thinking of where allowing access to these protected members could break the nature of protected members?

I believe it's to prevent one derived class from messing with a sibling derived class's invariants. Consider

class A {
    protected: void foo();
};
class B : public A {
    // complicated code
};
class C : public A {
    void bar(B* b) {
        b->foo();
    }
};

This effectively allows C to modify just the A subobject of B, which could violate invariants imposed by B on its A subobject, which C cannot be expected to know about.

Community
  • 1
  • 1
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
0

Funny story. I just got a response from Bjarne on this subject. Here is his response.

Consider

class B : public base
{
public:
    // ...
};

A a;
B b;
a.f(&b); // a manipulated b's base

This caused subtle bugs. This works:

class A : public base
{
public:
    void fa(base* pb)
    {
        f();     // use your own base
    }
};
cppguy
  • 3,611
  • 2
  • 21
  • 36