Опубликован: 10.10.2006 | Уровень: специалист | Доступ: свободно
Лекция 1:

Краткий обзор С++

1.5.3 Множественное наследование

Если класс A является базовым классом для B, то B наследует атрибуты A. т.е. B содержит A плюс еще что-то. С учетом этого становится очевидно, что хорошо, когда класс B может наследовать из двух базовых классов A1 и A2. Это называется множественным наследованием.

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

class my_displayed_task: public displayed, public task
{
// текст пользователя
       };

       class my_task: public task {
// эта задача не изображается
// на экране, т.к. не содержит класс displayed
// текст пользователя
       };

       class my_displayed: public displayed
       {
// а это не задача
// т.к. не содержит класс task
// текст пользователя
};

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

Все неоднозначности выявляются на стадии трансляции:

class task
{
public:
  void trace ();
  // ...
};

class displayed
{
public:
  void trace ();
  // ...
};

class my_displayed_task:public displayed, public task
{
  // в этом классе trace () не определяется
};

void g ( my_displayed_task * p )
{
  p -> trace ();  // ошибка: неоднозначность
}

В этом примере видны отличия С++ от объектно-ориентированных диалектов языка Лисп, в которых есть множественное наследование. В этих диалектах неоднозначность разрешается так: или считается существенным порядок описания, или считаются идентичными объекты с одним и тем же именем в разных базовых классах, или используются комбинированные способы, когда совпадение объектов для базовых классов сочетается с более сложным способом для производных классов. В С++ неоднозначность, как правило, разрешается введением еще одной функции:

class my_displayed_task:public displayed, public task
  {
      // ...
  public:
      void trace ()
      {
        // текст пользователя
        displayed::trace ();  // вызов trace () из displayed
        task::trace ();       // вызов trace () из task
      }
       // ...
  };

 void g ( my_displayed_task * p )
{
       p -> trace ();  // теперь нормально
}

1.5.4 Инкапсуляция

Пусть члену класса (неважно функции-члену или члену, представляющему данные) требуется защита от "несанкционированного доступа". Как разумно ограничить множество функций, которым такой член будет доступен? Очевидный ответ для языков, поддерживающих объектно-ориентированное программирование, таков: доступ имеют все операции, которые определены для этого объекта, иными словами, все функции-члены. Например:

class window
{
// ...
protected:
   Rectangle inside;
   // ...
};

class dumb_terminal : public window
{
   // ...
public:
   void prompt ();
   // ...
};

Здесь в базовом классе window член inside типа Rectangle описывается как защищенный (protected), но функции-члены производных классов, например, dumb_terminal::prompt(), могут обратиться к нему и выяснить, с какого вида окном они работают. Для всех других функций член window::inside недоступен.

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

Неочевидное следствие из этого: нельзя составить полный и окончательный список всех функций, которым будет доступен защищенный член, поскольку всегда можно добавить еще одну, определив ее как функцию-член в новом производном классе. Для метода абстракции данных такой подход часто бывает мало приемлемым. Если язык ориентируется на метод абстракции данных, то очевидное для него решение - это требование указывать в описании класса список всех функций, которым нужен доступ к члену. В С++ для этой цели используется описание частных (private) членов. Оно использовалось и в приводившихся описаниях классов complex и shape.

Важность инкапсуляции, т.е. заключения членов в защитную оболочку, резко возрастает с ростом размеров программы и увеличивающимся разбросом областей приложения. В \S 6.6 более подробно обсуждаются возможности языка по инкапсуляции.

1.6 Пределы совершенства

Язык С++ проектировался как "лучший С", поддерживающий абстракцию данных и объектно-ориентированное программирование. При этом он должен быть пригодным для большинства основных задач системного программирования.

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

  • идти на традиционных машинах;
  • сосуществовать с традиционными операционными системами и языками;
  • соперничать с традиционными языками программирования в эффективности выполнения программы;
  • быть пригодным во всех основных областях приложения.

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

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

Если не вводить в язык возможности низкого уровня, то придется для основных задач большинства областей приложения использовать некоторые языки низкого уровня, например С или ассемблер. Но С++ проектировался с расчетом, что в нем можно сделать все, что допустимо на С, причем без увеличения времени выполнения. Вообще, С++ проектировался, исходя из принципа, что не должно возникать никаких дополнительных затрат времени и памяти, если только этого явно не пожелает сам программист.

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

С++ проектировался для поддержки того принципа, что всякая программа есть модель некоторых существующих в реальности понятий, а класс является конкретным представлением понятия, взятого из области приложения ( \S 12.2). Поэтому классы пронизывают всю программу на С++, и налагаются жесткие требования на гибкость понятия класса, компактность объектов класса и эффективность их использования. Если работать с классами будет неудобно или слишком накладно, то они просто не будут использоваться, и программы выродятся в программы на "лучшем С". Значит пользователь не сумеет насладиться теми возможностями, ради которых, собственно, и создавался язык.

Равиль Ярупов
Равиль Ярупов
Привет !
Федор Антонов
Федор Антонов
Оплата и обучение
Роман Островский
Роман Островский
Украина
Оксана Пагина
Оксана Пагина
Россия, Москва