3

I am trying to fully understand member access rules defined in multiple paragraphs of the [class.access] section of the C++ standard. They are quite complex or even confusing therefore I need a short but accurate and exhaustive summary.

I compiled this program to test the accessibility of protected members in several situations (since the rules for protected members are the most complex):1

#include <iostream>

class B {
  protected:
    int i = 1;
    static int const I = 1;
};

class X: public B {
  protected:
    int j = 2;
    static int const J = 2;
  public:
    void f();
    friend void g();
};

class D: public X {
  protected:
    int k = 3;
    static int const K = 3;
};

void X::f() {
  B b;
  X x;
  D d;
  //std::cout << b.i;  // error: 'i' is a protected member of 'B'
  std::cout << b.I;
  std::cout << x.i;
  std::cout << x.I;
  std::cout << x.j;
  std::cout << x.J;
  std::cout << d.i;
  std::cout << d.I;
  std::cout << d.j;
  std::cout << d.J;
  //std::cout << d.k;  // error: 'k' is a protected member of 'D'
  //std::cout << d.K;  // error: 'K' is a protected member of 'D'
}

void g() {
  B b;
  X x;
  D d;
  //std::cout << b.i;  // error: 'i' is a protected member of 'B'
  //std::cout << b.I;  // error: 'I' is a protected member of 'B'
  std::cout << x.i;
  std::cout << x.I;
  std::cout << x.j;
  std::cout << x.J;
  std::cout << d.i;
  std::cout << d.I;
  std::cout << d.j;
  std::cout << d.J;
  //std::cout << d.k;  // error: 'k' is a protected member of 'D'
  //std::cout << d.K;  // error: 'K' is a protected member of 'D'
}

int main() {
  B b;
  X x;
  D d;
  //std::cout << b.i;  // error: 'i' is a protected member of 'B'
  //std::cout << b.I;  // error: 'I' is a protected member of 'B'
  //std::cout << x.i;  // error: 'i' is a protected member of 'B'
  //std::cout << x.I;  // error: 'I' is a protected member of 'B'
  //std::cout << x.j;  // error: 'j' is a protected member of 'X'
  //std::cout << x.J;  // error: 'J' is a protected member of 'X'
  //std::cout << d.i;  // error: 'i' is a protected member of 'B'
  //std::cout << d.I;  // error: 'I' is a protected member of 'B'
  //std::cout << d.j;  // error: 'j' is a protected member of 'X'
  //std::cout << d.J;  // error: 'J' is a protected member of 'X'
  //std::cout << d.k;  // error: 'k' is a protected member of 'D'
  //std::cout << d.K;  // error: 'K' is a protected member of 'D'
  return 0;
}

I came to this conclusion on direct accessibility:2

  • public members of a class are directly accessible to any entities (cf. [class.access/base-5.1]);
  • private members of a class are directly accessible only to members and friends of that class (cf. [class.access/base-5.2]);
  • protected members of a class are directly accessible only to members and friends of that class (cf. [class.access/base-5.3]), to members and friends of the base classes of that class if those protected members are inherited from those base classes or the base classes of those base classes (cf. [class.access/base-5.4]),3 and to members of the derived classes of that class (cf. [class.access/base-5.3]) if those protected members are not non-static data members nor non-static member functions (cf. [class.access/class.protected-1]).

Is my summary correct?


1 I used the Clang 9.0.0 compiler with C++ 17.

2 Access to a member i of a class B can be either direct, that is through that class: b.i (direct access), or indirect, that is through a derived class D of that class: d.i (inheritance access). Since members inherited by a derived class are members of that derived class with changes in their accessibility (cf. [class.access/base-1]), inheritance access to a member of a class can be treated as direct access to the inherited member of the derived class of that class. In other words, only direct access needs to be considered.

3 My clause here differs slightly from the referenced clause of the standard [class.access/base-5.4]:

A member m is accessible at the point R when named in class N if

  • there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B.

This is intended as the compiler behaves differently and my feeling is that the compiler is right. In my opinion there are two problems with the clause of the standard:

  • the access point R should be restricted to members and friends of class B (which is what the compiler does by raising errors for the d.* accesses in main in the program);
  • the member m in class N should be restricted to be inherited from class B, not overridden by class N (which is what the compiler would have done by raising errors for the d.i, d.I, d.j, and d.J accesses in X::f and g, had i, I, j and J been overridden in D in the program).
Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
  • What do you mean by *and to members of base classes of that class if the protected members are static.* A base class gets no special access to things that derive from it. – NathanOliver Feb 13 '20 at 21:06
  • Here you can find good summary of access qualifiers https://stackoverflow.com/questions/860339/difference-between-private-public-and-protected-inheritance – Tarek Dakhran Feb 13 '20 at 21:14
  • @NathanOliver Oops, I inverted everything. Updated, thanks. – Géry Ogam Feb 13 '20 at 21:16
  • @TarekDakhran My post is not about the *inheritance* of access specifiers, which is rather straightforward. It is about the *semantics* of access specifiers. – Géry Ogam Feb 13 '20 at 22:01
  • The standard is intended to be "accurate and exhaustive". True, it's not exactly short, but that's a direct result of trying to be both accurate and exhaustive. Your summary misses the effect of private and protected inheritance, for instance. – MSalters Feb 14 '20 at 09:36
  • @MSalters How these effects affect my summary? – Géry Ogam Feb 14 '20 at 10:38
  • @Maggyero: Not all entities can access a public member of a private base class. – MSalters Feb 14 '20 at 10:40
  • @MSalters You are actually not accessing a public member of the base class but a *private* member *of the derived class* (cf. [\[class.access/base-1\]](http://eel.is/c++draft/class.access#base-1): "If a class is declared to be a base class for another class using the `private` access specifier, the public and protected members of the base class are accessible as private members of the derived class."). So this does not affect my public clause (and note that the standard public clause [\[class.access/base-5.1\]](http://eel.is/c++draft/class.access#base-5.1) states the exact same thing) – Géry Ogam Feb 14 '20 at 13:26
  • @MSalters I have updated the post to emphasise the point of view adopted in the conclusion which treats *inheritance access* to a member of a class as *direct access* to a member of the derived class of that class. – Géry Ogam Feb 15 '20 at 17:53

2 Answers2

0

If your question is based on access, these are the rules within c++. I will do a basic summary below, but for the exhaustive explanation please go here. This will go into greater detail on how each works.

public
A public member of a class is accessible anywhere

protected
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

private
A private member of a class is only accessible to the members and friends of that class, regardless of whether the members are on the same or different instances

To see examples please go to the link above.

With nested classes you are within scope of that the base class, so private and protected members are able to accessed. If the member is static, you will be able access directly, otherwise the object of that class will have to be constructed in order to access those members within that class. Here is an example from class X above:

class X: public B {
public:
    class A {
    public:
        void b() {
            std::cout << J << std::endl;
            std::cout << S << std::endl;
        }
        void d(X x) {
            std::cout << x.j << std::endl;
            std::cout << x.s << std::endl;
        }
    };

    void f();

protected:
    int j = 2;
    static int const J = 2;
private:
    friend void g();
    int s = 3;
    static int const S = 4;
};

Here is what is how public, protected, and private is meant when using them for inheritance

public
When a class uses public member access specifier to derive from a base, all public members of the base class are accessible as public members of the derived class and all protected members of the base class are accessible as protected members of the derived class (private members of the base are never accessible unless friended)

protected
When a class uses protected member access specifier to derive from a base, all public and protected members of the base class are accessible as protected members of the derived class (private members of the base are never accessible unless friended)
private
When a class uses private member access specifier to derive from a base, all public and protected members of the base class are accessible as private members of the derived class (private members of the base are never accessible unless friended).

Note: Derived classes inherits all methods of the base class with the following exception.

  • Constructors, destructors and copy constructors of the base class

  • Overloaded operators such of the base class -- These may not act as you would expect, and should be implemented in a way that you override for each operator for each class.

  • The friend functions of the base class.

Now as for the friend specifier, this is from the cpp reference here Here you will have examples and detailed explanations on how to use it.

You will also find examples for a lot of information on there when it comes to the standard library, also you will be able to see what is coming to the standard in the future and what features your compiler supports.

  • It's not a complete answer. Private and protected members of an outer class are accessible from a nested class: http://eel.is/c++draft/class.access.nest#1 – 273K Feb 14 '20 at 08:32
  • "**protected** - functions and variables are able to be accessed in the same class, and derived class" You are confusing member inheritance and member accessibility. Protected members of a class are not *accessible* (as `base.member`) to derived classes—but the *inherited* members *of the derived classes* are (as `derived.member`)—, unless they are not non-static data members nor non-static member functions (cf. [\[class.access/class.protected-1\]](http://eel.is/c++draft/class.access#class.protected-1)). – Géry Ogam Feb 14 '20 at 15:28
  • Overloaded operators which are member functions are inherited exactly like any other member function. (The one catch is that every class has at least one implicitly declared `operator=`, which hides the base `operator=` functions, but they are still inherited members.) – aschepler Feb 14 '20 at 23:39
  • That is more what I was getting at, but should have been more clear. As for the operators, I believe there are special cases, at least when I learned about that awhile back ago. I did forget to include nested classes and can add that in. As for the protected member, that is what I was getting at and should have been more clear there. – Brandon Manning Feb 15 '20 at 12:42
  • As for the nested class, the would be within the scope of.the base class. – Brandon Manning Feb 15 '20 at 15:22
  • @BrandonManning You are still mixing *direct access* and *inheritance access* in your first paragraph. But I recognize this is tricky, even the standard itself makes the confusion (like here: [\[class.access#1\]](http://eel.is/c++draft/class.access#1)). You should either talk about direct access (like here: [\[class.access/base-5\]](http://eel.is/c++draft/class.access#base-5)) or inheritance access (like here: [\[class.access/base-1\]](http://eel.is/c++draft/class.access#base-1)), not both at the same time. – Géry Ogam Feb 15 '20 at 17:30
  • Yes that is my fault, I will update that for direct access. I am traveling and have been using my phone. I will update that once I get back home to make it clearer. But one of the key things is when deciding between private and protected is how you want access in terms of inheritance. – Brandon Manning Feb 16 '20 at 23:03
-1

You mist a not so intuitive access, c++ is compile dependent and one of the effects is that all instances of are the "same" in memory and can access each other private variable if a function on that class can accept references of "himself", for more details check about the class variable this.

#include <iostream>

class MyClass{

    int num = 69;

    public:

        void set_num(int n){
            num = n;
        };
        int get_num() {
            return num;
        }

        int get_num_from(MyClass * ptr_ref) {
            return ptr_ref->num;
        }
        int get_num_from(MyClass ref) {
            return ref.num;
        }

};


int main(){

    MyClass class_a;
    MyClass class_b;

    class_a.set_num(0);

    std::cout << "class_a -> " << class_a.get_num() << std::endl;
    std::cout << "class_b ref -> " << class_b.get_num_from(class_a) << std::endl;
    std::cout << "class_b ptr_ref -> " << class_b.get_num_from(&class_a) << std::endl;

}

Output:

class_a -> 0
class_b ref -> 0
class_b ptr_ref -> 0
SrPanda
  • 854
  • 1
  • 5
  • 9