Множество уровней частного и государственного наследования - необычный доступ

#include<iostream>
using namespace std;
class uvw;
class abc{
   private:
      int privateMember;
   protected:
    int protMember;
   public:
    int publicMember;
};

class def : private abc{
    public:
       void dummy_fn();
};

class uvw: public def{

};

void def::dummy_fn()
{
   abc x;
   def y;
   uvw z;
   cout << z.protMember << endl; // This can be accessed and doesn't give a compile-error
}

Из того, что я понимаю, после def наследует конфиденциально от abc, protMember и publicMember становится закрытым в def. Итак, теперь, когда uvw наследуется от def, он не должен иметь никаких элементов данных. Но мы можем странно получить доступ к z.protMember из dummy_fn(), где в качестве z не должно быть переменной protMember. Я где-то ошибаюсь?

Ответ 1

Если вы пытаетесь получить к нему доступ из свободной функции, это не сработает. Он работает в этом случае, потому что dummy_fn() является функцией-членом def, поэтому он имеет доступ ко всем частным вещам внутри def. Поскольку z is-a def, он имеет доступ к частным членам def внутри экземпляра z.

Или, по крайней мере, это моя догадка. Это странный случай.

Ответ 2

Вы случайно наткнулись на то, что С++ статически (и не динамически) набрал; в результате проверки доступа выполняются во время компиляции (не время выполнения) и, следовательно, причина видимого типа переменной, а не ее фактический динамический тип (который является свойством среды выполнения).

Чтобы немного распутать пример:

class Base { protected: int prot; };

class Derived: private Base { void func(); };

void Derived::func() { std::cout << prot << std::endl; }

func является членом Derived, в результате он может обращаться ко всем членам данных Derived, как прямым, так и доступным через наследование:

  • Derived напрямую наследуется от Base, поэтому Base доступен
  • prot protected в Base, поэтому prot доступен любому ребенку, который имеет доступ к Base

и, следовательно, в результате prot доступен в Derived (и, следовательно, в Derived::func).


Давайте продемонстрируем важность пути доступа:

class Another: private Base {};

class YetAnother: public Another { void func(); };

void YetAnother::func() { std::cout << prot << std::endl; } // ERROR (access)

Здесь, хотя Base::prot доступен в Another, поскольку Another скрывает тот факт, что он наследует от Base для всех остальных, YetAnother не может получить доступ к Base и, таким образом, транзитивно, не может получить доступ к Base::prot.


Давайте продемонстрируем эффект статического ввода:

class Basic { void func(); };

class More: public Basic { public: int a; };

void Basic::func() { std::cout << a << std::endl; } // ERROR (unknown)

Здесь, хотя объект More будет иметь член a, когда мы скомпилируем Basic, мы можем полагаться только на то, что знает Basic. И Basic не знает a.

Контрастируйте это с динамическим языком, таким как Python, где это будет нормально работать для объектов класса More и fail (AttributeError exception) для тех, у кого нет a.

Ответ 3

Частное наследование ограничивает доступ только за пределами класса. Он не ограничивает то, что видит производный класс из базового класса. Таким образом, в вашем случае, когда def наследует конфиденциально от abc, def будет по-прежнему иметь доступ ко всем защищенным членам abc. Только клиенты def не смогут получить доступ к чему-либо из abc; даже publicMember.

Кроме того, не путайте личное наследование с не наследующими членами или чем-то.

Итак, теперь, когда uvw наследуется от def, он не должен иметь никаких элементов данных.

Это утверждение неверно. uvw также имеет любые члены данных, которые abc и def имеют, они просто недоступны извне.

См. здесь для деталей: http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/