Base class as method argument

In modern object-oriented programming languages, protected members had always been treated as a kind of necessary evil, as they have more disadvantage than advantage. Almost all coding style guides will discourage you from using them. In this post, I’ll show another issue related to protected members which at first may seem unexpected behaviour, but on the other hand it is perfectly reasonable.

Let’s start with a short example, consider the following C++ code excerpt:

class Base {
protected:
    int prot;

public:
    int getProt() {
        return prot;
    }
};

class Derived : public Base {
private:
    int priv;

public:
    void setThat(Derived* derived) {
        derived->prot = this->prot;
    }

    void setThis(Base* base) {
        this->prot = base->prot;
    }
};

What you see here is two classes, Base with a protected attribute prot, and Derived which inherits this protected attribute from its base class. The two methods, setThis and setThat both access the protected member prot of their argument, the only difference is the static type of the argument (Base* and Derived*, respectively). At first sight, there is no problem with this code. However, when you try to compile it, the compiler will issue an error (I used gcc in this example, but I also tried MSVC, the result was the same but the error message was not so helpful):

test.cpp: In member function ‘void Derived::setThis(Base*)’:
test.cpp:4: error: ‘int Base::prot’ is protected
test.cpp:22: error: within this context

Why is that error message, you could ask, what’s wrong? Base::prot is protected, Derived inherits from Base, so methods in Derived should be able to access protected attributes from Base. That’s right, but there is a catch. If you look at the error message, you will see that the error is caused by the setThis method, the setThat method compiles just fine.

Deriving from a base class does not mean that the derived class will be able to access the protected members of the base class. It means that the derived class will be able to access its own members that were inherited from the base class and have at least protected visibility.

This is why the setThat method compiles without any issues. It accesses the protected member from the same class (Derived) where the method resides, this would also work with private members (priv, for example). On the other hand, setThis tries to access a protected member in Base, which is not allowed by the otherwise obvious visibility rules.

I was curious and also tried the same code in C# and Java (after trimming it according to the language specifics, of course). In C# the result was the same, as I expected. But surprisingly, the Java compiler happily compiled the source code without any error. At first I thought that maybe Java treated protected members differently than the other two languages, but then I realized that the classes were in the same package. And because in Java the default (package-level) visibility is stronger than the protected visibility, Derived was able to access the prot attribute regardless whether it is protected or not. This is yet another reason to use protected members with caution: in Java, they are visible from outside of the package, and this is usually not desired.

Back to the example, all I had to do is to separate the two classes to different packages. This is the Base class:

package base;

public class Base {
    protected int prot;

    public int getProt() {
        return prot;
    }
}

And here comes the source code of the Derived class:

package derived;

import base.Base;

public class Derived extends Base {
    private int priv;

    public void setThat(Derived derived) {
        derived.prot = this.prot;
    }

    public void setThis(Base base) {
        this.prot = base.prot;
    }
}

Now that package visibility was out of scope, I was able to reproduce the expected behaviour with the Java compiler as well:

derived\Derived.java:13: prot has protected access in base.Base
            this.prot = base.prot;
                            ^
1 error

I don’t agree that protected members are evil, but there are inevitably some issues around them that have to be considered when designing classes and object-oriented programs.

Leave a Reply

Your email address will not be published.

*