Home > Software engineering >  Is an object considered an "outside object" even if it's being used in its own class?
Is an object considered an "outside object" even if it's being used in its own class?

Time:06-11

#include <iostream>
using namespace std;
class B {
protected:
    int x;

public:
    B(int i = 28) { x = i; }
    virtual B f(B ob) { return x   ob.x   1; }
    void show() { cout << x; }
};
class D : public B {
public:
    D(int i = -32)
        : B(i)
    {
    }
    B f(B ob) { return x   ob.x - 1; } //line 19
};
int main()
{
    B *p1 = new D, *p2 = new B, *p3 = new B(p1->f(*p2));
    p3->show();
    return 0;
}

The compiler error:

(line 19) int B::x is protected within this context, declared protected

I'm trying to understand why ob shows up as protected even though I'm using it in its own class. Are all objects excluding 'this' subject to being private/protected? Sorry for poor wording. Thank you in advance.

CodePudding user response:

From cppreference on "Protected Member Access" (emphasis mine)

A protected member of a class is only accessible

  1. to the members and friends of that class;
  2. to the members and friends (until C 17) of any derived class of that class, but only when the class of the object through which the protected member is accessed is that derived class or a derived class of that derived class

So subclasses cannot access protected members of other instances of the parent class.

CodePudding user response:

Your intuition might be better if you think about the case where there is more than one derived class. Here is an example where there are two derived classes, each with different ideas of how the protected member (x) should be used. (Pretend there are other members that have been omitted for simplicity.)

class Base {
  protected:
    // Base will reset x to zero under certain circumstances.
    // Otherwise, it is for derived classes to use.
    int x = 0;
};

struct Derived : public Base {
    // Derived chooses to use negative values for x.
    Derived() : Base() { x--; }
    void update(Derived& other) {
        other.x = x;
    }
};

struct Alternative : public Base {
    // Alternative chooses to use positive values for x.
    Alternative() : Base() { x  ; }
    void update(Alternative& other) {
        other.x = x;
    }
};

This much is good. It compiles, because unlike the question's code, Derived accesses x only in Derived objects, not in Base objects. Derived objects never have a positive x, and Alternative objects never have a negative x.

What happens if we change the type of update's parameter to Base&? Compilation fails, as in the question. Why? Well, you do not want a situation like the following:

    Derived negative;
    Alternative positive;
    negative.update(positive); // <-- problem!

If Derived::update() were to accept a Base& instead of a Derived&, then the above would also compile. An object can be converted to a reference to an unambiguous base class, so positive would be an acceptable argument. However, update would proceed to set positive.x to negative.x, breaking the assumption Alternative makes about x never being negative. Breaking another class is Bad.

This example is contrived for simplicity. yet the principle is there. Sibling classes are not friends; they often are not even aware of each other's existence. A class cannot be allowed to mess with a different class. Even though a derived class might have some protected access to its base, it can only do so when it is known that the object in question is actually the same class, not some other derived class.

  • Related