Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

void call(const B& b)

// класс D — разновидность класса B,

// поэтому функция call может

// получить объект класса D

// класс DD — разновидность класса D,

// а класс D — разновидность класса B,

// поэтому функция call может получать объект класса DD

{

b.f;

b.g;

}

int main

{

B b;

D d;

DD dd;

call(b);

call(d);

call(dd);

b.f;

b.g;

d.f;

d.g;

dd.f;

dd.g;

}

В

результате выполнения этой программы получим следующее:

B::f B::g D::f B::g D::f B::g B::f B::g D::f D::g DD::f DD::g

Если вы понимаете, почему, то знаете механизмы наследования и виртуальных функций.

14.3.4. Доступ

Язык С++ реализует простую модель доступа к членам класса. Члены класса могут относиться к следующим категориям.

• Закрытые (private). Если член класса объявлен с помощью ключевого слова

private
, то его имя могут использовать только члены данного класса.

• Защищенные (protected). Если член класса объявлен с помощью ключевого слова

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

• Открытые (public). Если член класса объявлен с помощью ключевого слова

public
, то его имя могут использовать все функции.

Изобразим это на рисунке.

Базовый класс также может иметь атрибут

private
,
protected
или
public
.

• Если базовый класс для класса

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

• Если базовый класс для класса

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

• Если базовый класс для класса

D
является открытым, то имена его открытых членов могут использоваться любыми функциями.

Эти определения игнорируют понятие дружественной функции или класса и другие детали, которые выходят за рамки рассмотрения нашей книги. Если хотите стать крючкотвором, читайте книги Stroustrup, The Design and Evolution of C++ (Страуструп, “Дизайн и эволюция языка С++”), The C++ Programming Language (Страуструп, “Язык программирования С++”) и стандарт 2003 ISO C++. Мы не рекомендуем вам становиться крючкотвором (т.е. вникать в мельчайшие детали языковых определений) — быть программистом (разработчиком программного обеспечения, инженером, пользователем, назовите как хотите) намного увлекательнее

и полезнее для общества.

14.3.5. Чисто виртуальные функции

Абстрактный класс — это класс, который можно использовать только в качестве базового. Абстрактные классы используются для представления абстрактных понятий; иначе говоря, мы используем абстрактные классы для описания понятий, которые являются обобщением общих характеристик связанных между собой сущностей. Описанию абстрактного понятия (abstract concept), абстракции (abstraction) и обобщению (generalization) посвящены толстенные книги по философии. Однако философское определение абстрактного понятия мало полезно. Примерами являются понятие “животное” (в противоположность конкретному виду животного), “драйвер устройства” (в противоположность драйверу конкретного вида устройств) и “публикация” (в противоположность конкретному виду книг или журналов). В программах абстрактные классы обычно определяют интерфейсы групп связанных между собой классов (иерархии классов).

В разделе 14.2.1 мы видели, как создать абстрактный класс, объявив его конструктор в разделе protected. Существует другой — более распространенный — способ создания абстрактного класса: указать, что одна или несколько его виртуальных функций будет замещена в производном классе. Рассмотрим пример.

class B { // абстрактный базовый класс

public:

virtual void f=0; // чисто виртуальная функция

virtual void g=0;

};

B b; // ошибка: класс B — абстрактный

Интересное обозначение

=0
указывает на то, что виртуальные функции
B::f
и
B::g
являются чистыми, т.е. они должны быть замещены в каком-то производном классе. Поскольку класс B содержит чисто виртуальную функцию, мы не можем создать объект этого класса. Замещение чисто виртуальных функций устраняет эту проблему.

class D1:public B {

public:

void f;

void g;

};

D1 d1; // OK

Несмотря на то что все чисто виртуальные функции замещаются, результирующий класс остается абстрактным.

class D2:public B {

public:

void f;

// no g

};

D2 d2; // ошибка: класс D2 — (по-прежнему) абстрактный

class D3:public D2 {

public:

void g;

};

D3 d3; // OK

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

Поделиться с друзьями: