friend void clobber(Sneaky&); // есть доступ к Sneaky::prot_mem

 friend void clobber(Base&);   // нет доступа к Base::prot_mem

 int j;                        // j по умолчанию закрытая

};

// ok: clobber может обращаться к закрытым и защищенным членам Sneaky

void clobber(Sneaky &s) { s.j = s.prot_mem = 0; }

// ошибка: clobber не может обращаться к защищенным членам Base

void clobber(Base &b) { b.prot_mem = 0; }

Если производные классы (и друзья) смогут обращаться к защищенным членам в объекте базового класса, то вторая версия функции clobber (получающая тип Base&) будет корректна. Хоть эта функция и не дружественна классу Base, она все же сможет изменить объект типа Base; для обхода защиты спецификатором protected любого класса достаточно определить новый класс по линии Sneaky.

Для предотвращения такого способа применения члены и друзья производного класса могут обращаться к защищенным членам только тех объектов базового класса, которые встроены в объект производного; к обычным объектам базового типа у них никакого доступа нет.

Открытое, закрытое и защищенное наследование

Доступ к члену наследуемого класса контролируется комбинацией спецификатора доступа этого члена в базовом классе и спецификатором доступа в списке наследования производного класса. Для примера рассмотрим следующую иерархию:

class Base {

public:

 void pub_mem(); // открытый член

protected:

 int prot_mem;   // защищенный член

private:

 char priv_mem;  // закрытый член

};

struct Pub_Derv : public Base {

 // ok: производный класс имеет доступ к защищенным членам

 int f() { return prot_mem; }

 // ошибка: закрытые члены недоступны производным классам

 char g() { return priv_mem; }

};

struct Priv_Derv : private Base {

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

 int f1() const { return prot_mem; }

};

Спецификатор доступа наследования никак не влияет на возможность членов (и друзей) производного класса обратиться к членам его собственного прямого базового класса. Доступ к членам базового класса контролируется спецификаторами доступа в самом базовом классе. Структуры Pub_Derv и Priv_Derv могут обращаться к защищенному члену prot_mem, но ни одна из них не может обратиться к закрытому члену priv_mem.

Задача спецификатора доступа наследования — контролировать доступ пользователей производного класса, включая другие классы, производные от него, к членам, унаследованным от класса Base:

Pub_Derv d1;  // члены, унаследованные от Base, являются открытыми

Priv_Derv d2; // члены, унаследованные от Base, являются закрытыми

d1.pub_mem(); // ok: pub_mem является открытой в производном класс

d2.pub_mem(); // ошибка: pub_mem является закрытой в производном классе

Структуры Pub_Derv и Priv_Derv унаследовали функцию pub_mem(). При открытом наследовании члены сохраняют свой спецификатор доступа. Таким образом, объект d1 может вызвать функцию pub_mem(). В структуре Priv_Derv члены класса Base являются закрытыми; пользователи этого класса не смогут вызвать функцию pub_mem().

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

struct Derived_from_Public : public Pub_Derv {

 // ok: Base::prot_mem остается защищенной в Pub_Derv

 int use_base() { return prot_mem; }

};

struct Derived_from_Private : public Priv_Derv {

 // ошибка: Base::prot_mem является закрытой в Priv_Derv

 int use_base() { return prot_mem; }

};

Перейти на страницу:

Похожие книги