2

I am studying this code:

#include <iostream>

class A
{
public:
    A() {};

    void fox(A& otherA) { otherA.hello(); }

protected:
    void hello() {std::cout << "hello" << std::endl;}
};

A a1,a2;

int main(void)
{
    a1.fox(a2);
    a1.hello();
}

and am a bit confused as to how saying a1.fox(a2) will compile while saying a1.hello() will not. I would have assumed it would break because while I can call protected and private functions from within a class...allowing me to do that on the otherA object means I would have to be aware that I am calling it from within a member of it's own class. Why/how does this work?

Palace Chan
  • 8,845
  • 11
  • 41
  • 93

3 Answers3

6

Protected is not instance-by-instance access control, it's a class-by-class access control.

main() (which is not in any class) cannot call private or protected methods of any class at all; hence it fails to call a1.hello().

The implementation of A::fox(A&), on the other hand, is inside the class A, so it can call its private and protected methods, both on itself and on other instances of the class.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

Because A is making the call to otherA.hello();. You cannot call into it private or protected code directly, but descendants of A, including A itself, can call into A (descendants cannot access A's private data/methods, but A can access A's private data/methods). Relatedly, A can also access the private data/methods of other instances of A (this is generally how copy constructors and assignment operators work).

In fact, it's the ability to use protected code within the class that enables the creation of some very powerful OO concepts. With that said, I see it as an abuse to call an inner-object's private or protected methods even when possible, but using them directly against yourself is hopefully by design rather than convenience.

In C++, you can provide abstract implementations of classes (or structs for that matter) by marking items as virtual and not providing an implementation.

class A
{
public:
    void set_value(const std::string &value)
    {
        if (is_valid(value))
        {
            this->value = value;
        }
    }

    const std::string &get_value() const
    {
        return value;
    }
protected:
    virtual boolean is_valid(const std::string &value) const = 0;
private:
    std::string value;
}

Then

class B : public A
{
protected:
    virtual boolean is_valid(const std::string &value) const
    {
        return value != "abc";
    }
}

Notice that A is setup to inherit functionality, and it is built to anticipate it being supplied (granted the example above doesn't really provide a lot of utility, but it does show what I mean).

pickypg
  • 22,034
  • 5
  • 72
  • 84
0

The access control in C++ is at class level. Not instance level.

Compiler can't possibly know if otherA is actually the same instance or not. You need runtime support to actually determine the identity of the instance to provide such access control.

Apart from the performance problem that this presents, the objective of C++ is to catch errors at compile time, not execution time. As stroustrup says, data-encapsulation and type-safety features can't prevent someone from doing fraud (when the program has unmediated access to all allocated memory).

Now, why is it ok to settle for a solution without runtime check. An anology could help - because as an implementor of a class, you won't rob your own house.

Chethan
  • 905
  • 1
  • 10
  • 18