• R.11.1 Спецификации доступа
  • R.11.2 Спецификации доступа для базовых классов
  • R.11.3 Описания доступа
  • R.11.4 Друзья
  • R.11.5 Доступ к защищенным членам
  • R.11.6 Доступ к виртуальным функциям
  • R.11.7 Множественный доступ
  • R.11 Контроль доступа к членам

    Член класса может быть:

    • частным (private); это значит, что его имя можно использовать только в функциях-членах и друзьях класса, в котором он описан;

    • защищенным (protected); это значит, что его имя можно использовать только в функциях-членах и друзьях класса, в котором он описан, а также в функциях-членах и друзьях классов, являющихся производными по отношению к этому классу (см. §R.11.5);

    • общим (public); это значит, что его имя можно использовать в любой функции.

    Члены класса, описанного со служебным словом class, являются частными по определению. Члены класса, описанного со служебным словом struct или union, являются общими по определению, например:

    class X {

     int; // X:: частный по определению

    };


    struct S {

     int a; // S::a общий по определению

    };

    R.11.1 Спецификации доступа

    Описания членов могут быть снабжены спецификацией доступа (§R.10):

    спецификация-доступа: список-членов opt

    Спецификация-доступа задает правила доступа к членам, которые действуют до конца жизни класса или пока не появится другая спецификация-доступа, например,

    class X {

     int a; // X::a частный по определению: учитывается 'class'

    public:

     int b; // X::b общий

     int c; // X::c общий

    };

    Допустимо любое число спецификаций доступа и задавать их можно в любом порядке, например,

    struct S {

     int a; // S::a общий по определению: учитывается `struct'

    protected:

     int b; // S::b защищенный

    private:

     int c; // S::c частный

    public:

     int d; // S:: d общий

    };

    Порядок размещения членов, представляющих данные, которые имеют разные спецификации-доступа, определяется реализацией (§R.9.2).

    R.11.2 Спецификации доступа для базовых классов

    Если класс описан как базовый (§R.10) по отношению к другому классу с помощью спецификации доступа public, то члены со спецификацией public или protected из базового класса являются соответственно членами с той же спецификацией для производного класса. Если класс описан как базовый по отношению к другому с помощью спецификации доступа private, то члены со спецификацией public или protected из базового класса являются членами со спецификацией private для производного класса. Частные члены базового класса остаются недоступными даже для производных классов, если только для обеспечения доступа при описании базового класса не было использовано описание friend.

    Если для базового класса не указана спецификация-доступа, то для производного класса, если он описан как struct, предполагается спецификация public, а если он описан со служебным словом class, то - спецификация private, например:

    class B {/*… */};

    class D1: private B {/*… */};

    class D2: public B {/*… */};

    class D3: B {/*… */}; // `B' частный по определению

    struct D4: public B {/*… */};

    struct D5: private B {/*… */};

    struct D6: B {/*… */}; // `B' частный по определению

    Здесь класс является общим (public) базовым классом для D2, D4 и D6 и частным (private) базовым классом для D1, D2 и D5.

    Описание базового класса как private не влияет на доступ к статическим членам базового класса. Однако, если при обращении к статическому члену используется объект или указатель, который нужно преобразовывать, то действуют обычные правила преобразования указателей.

    В функциях-членах или друзьях класса X можно X* неявно преобразовывать в указатель на частный класс, являющийся непосредственно базовым по отношению к X.

    R.11.3 Описания доступа

    Используя уточненное имя, можно установить доступ к члену базового класса в части public или protected описания производного класса. Это называется описанием доступа.

    Приведем пример:

    class B {

     int a;

    public:

     int b, c;

     int bf();

    };


    class D: private B {

     int d;

     public:

     B::c; // adjust access to `B::c'

     int e;

     int df();

    };


    int ef(D&);

    Во внешней функции ef можно использовать только имена c, e, и df. Поскольку функция df член класса D, в ней можно использовать имена b, c, bf, d, e и df, но не a. Функция bf - член класса B и в ней можно использовать члены a, b, c и bf.

    Описания доступа не следует использовать для ограничения доступа к члену, доступному в базовом классе, также как не следует использовать его для обеспечения доступа к члену, который недоступен в базовом классе, например:

    class B {

    public:

     int a;

    private:

     int b;

    protected:

     int c;

    };


    class D: private B {

    public:

     B::a; // описать `a' как общий член D

     B::b; // ошибка: попытка расширить доступ,

      // `b' не может быть общим членом D

    protected:

     B::c; // описать `c' как защищенный член D

     B::a; // ошибка: попытка сузить доступ,

      // `a' не может быть защищенным членом D

    };

    Описание доступа для имени перегруженной функции устанавливает доступ в базовом классе ко всем функциям с этим именем, например:

    class X {

    public:

     f();

     f(int);

    };


    class Y: private X {

    public:

     X::f; // makes X::f() and X::f(int) public in Y

    };

    Нельзя в производном классе установить доступ к члену базового класса, если в производном классе определен член с этим же именем, например:

    class X {

    public:

     void f();

    };


    class Y: private X {

    public:

     void f(int);

     X::f; // ошибка: два описания f

    };

    R.11.4 Друзья

    Другом класса называется функция, которая не является членом класса, но в которой можно использовать частные и защищенные члены этого класса. Имя друга не принадлежит области видимости класса, и дружественная функция не вызывается с помощью операций доступа к членам (§R.5.2.4), если только она не является членом другого класса. Следующий пример показывает различие между членами и друзьями:

    class X {

     int a;

     friend void friend_set(X*, int);

    public:

     void member_set(int);

    };


    void friend_set(X* p, int i) {p-›a = i;}

    void X::member_set(int i) {a = i;}


    void f()

    {

     X obj;

     friend_set(&obj,10);

     obj.member_set(10);

    }

    Если в описании friend использовано имя перегруженной функции или операции, только функция, однозначно определяемая типами формальных параметров, становится другом. Функция-член класса X может быть другом класса Y, например:

    class Y {

     friend char* X::foo(int);

     //…

    };

    Можно объявить все функции класса X друзьями класса Y с помощью спецификации-сложного-типа (§R.9.1):

    class Y {

     friend class X;

     //…

    };

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

    class X {

     enum { a=100 };

     friend class Y;

    };


    class Y {

     int v[X::a]; // Y друг класса X

    };


    class Z {

     int v[X::a]; // ошибка: X::a недоступно

    };

    Если класс или функция, объявленные как друзья, не были описаны, их имена попадают в ту же область видимости, что и имя класса, содержащего описание friend (§R.9.1).

    Функция, появившаяся первый раз в описании friend, считается эквивалентной функции, описанной как extern (§R.3.3, §R.7.1.1).

    Если функция-друг определена в описании класса, она считается функцией со спецификацией inline и к ней применимо правило переноса определения функции для функций-членов (§R.9.3.2). Функция-друг, определенная в описании класса, относится на лексическом уровне к области видимости этого класса. Для функции-друга, определенной вне класса, это не так.

    На описание friend не влияет указание спецификаций-доступа (§R.9.2).

    Понятие дружбы не является ни наследуемым, ни транзитивным.

    Подтвердим это примером:

    class A {

     friend class B;

     int a;

    };


    class B {

     friend class C;

    };


    class C {

     void f(A* p);

     {

      p-›a++; // ошибка: C не друг класса A, хотя

       // является другом друга класса A

     }

    };


    class D: public B {

     void f(A* p)

     {

      p-›a++; // ошибка: D не друг класса A, хотя

       // является производным друга класса A

     }

    };

    R.11.5 Доступ к защищенным членам

    Друг или функция-член производного класса имеет доступ к защищенному статическому члену базового класса. Друг или функция-член производного класса могут получить доступ к защищенному нестатическому члену одного из своих базовых классов только через указатель, ссылку или объект производного класса (или любого класса, являющегося производным по отношению к нему). Рассмотрим пример:

    class B {

    protected:

     int i;

    };


    class D1: public B {

    };


    class D2: public B {

     friend void fr(B*, D1*, D2*);

     void mem(B*, D1*);

    };


    void fr(B* pb, D1* p1, D2* p2)

    {

     pb-›i = 1; // недопустимо

     p1-›i = 2; // недопустимо

     p2-›i = 3; // нормально (обращение через D2)

    }


    void D2::mem(B* pb, D1* p1)

    {

     pb-›i = 1; // недопустимо

     p1-›i = 2; // недопустимо

     i = 3; // нормально (обращение через this)

    }


    void g(B* pb, D1* p1, D2* p2)

    {

     pb-›i = 1; // недопустимо

     p1-›i = 2; // недопустимо

     p2-›i = 3; // недопустимо

    }

    R.11.6 Доступ к виртуальным функциям

    Правила доступа (§R.11) к виртуальной функции определяются ее описанием и на них не влияют правила доступа к к функции, которая позднее будет подавлять ее. Приведем пример:

    class B {

    public:

     virtual f();

    };


    class D: public B {

    private:

     f();

    };


    void f()

    {

     D d;

     B* pb = &d;

     D* pd = &d;


     pb-›f(); // нормально: B::f() общий член

      // вызывается D::f()

     pd-›f(); // ошибка: D::f() частный член

    }

    Права доступа проверяются при самом вызове, используя тип выражения, обозначающее объект, для которого вызывается функция-член (в примере выше это B*). Доступ к функции-члену в классе, где она определена (D в примере выше), в общем случае неизвестен.

    R.11.7 Множественный доступ

    Если добраться до имени можно несколькими путями по графу, задающему множественное наследование, то право доступа этого имени считается максимальным из прав, получаемых на разных путях. Поясним это примером:

    class W { public: void f(); };

    class A: private virtual W {};

    class B: public virtual W {};

    class C: public A, public B {

     void f() { W::f(); } // нормально

    };

    Поскольку W::f() доступно в C::f() по пути, связанному с общим наследованием из B, обращение является законным.








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх