Язык программирования C++ для профессионалов



         

Принадлежность и наследование - часть 3


class cfield : public field { // ... };

Это выражает тот факт, что cfield, действительно, есть сорта field, упрощает запись функции, которая использует член части field класса cfield, и, что самое главное, позволяет в классе cfield переопределять виртуальные функции из field. Загвоздка здесь в том, что преобразование cfield* к field*, встречающееся в определении класса cfield, позволяет обойти любой контроль доступа к field:

void q(cfield* p) { *p = "asdf"; // обращение к field контролируется // функцией присваивания cfield: // p->cfield::operator=("asdf")

field* q = p; // неявное преобразование cfield* в field* *q = "asdf"; // приехали! контроль обойден }

Можно было бы определить класс cfield так, чтобы field был его членом, но тогда cfield не может переопределять виртуальные функции field. Лучшим решением здесь будет использование наследования со спецификацией private (частное наследование):

class cfield : private field { /* ... */ }

С позиции проектирования, если не учитывать (иногда важные) вопросы переопределения, частное наследование эквивалентно принадлежности. В этом случае применяется метод, при котором класс определяется в общей части как производный от абстрактного базового класса заданием его интерфейса, а также определяется с помощью частного наследования от конкретного класса, задающего реализацию (§13.3). Поскольку наследование, используемое как частное, является спецификой реализации, и оно не отражается в типе производного класса, то его иногда называют "наследованием по реализации", и оно является контрастом для наследования в общей части, когда наследуется интерфейс базового класса и допустимы неявные преобразования к базовому типу. Последнее наследование иногда называют определением подтипа или "интерфейсным наследованием".

Для дальнейшего обсуждения возможности выбора наследования или принадлежности рассмотрим, как представить в диалоговой графической системе свиток (область для прокручивания в ней информации), и как привязать свиток к окну на экране. Потребуются свитки двух видов: горизонтальные и вертикальные. Это можно представить с помощью двух типов horizontal_scrollbar и vertical_scrollbar или с помощью одного типа scrollbar, который имеет аргумент, определяющий, является расположение вертикальным или горизонтальным. Первое решение предполагает, что есть еще третий тип, задающий просто свиток - scrollbar, и этот тип является базовым классом для двух определенных свитков. Второе решение предполагает дополнительный аргумент у типа scrollbar и наличие значений, задающих вид свитка. Например, так:




Содержание  Назад  Вперед