Читаем Программирование полностью

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


struct Circle:Shape {

  void draw_lines(int) const; // возможно, ошибка (аргумент int?)

  void drawlines() const;     // возможно, ошибка (опечатка в имени?)

  void draw_lines();          // возможно, ошибка (нет const?)

  // ...

};


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

Пример функции draw_lines() реален, и, следовательно, его трудно описать очень подробно, поэтому ограничимся чисто технической иллюстрацией замещения.


struct B {

  virtual void f() const { cout << "B::f "; }

  void g() const { cout << "B::g "; } // невиртуальная

};


struct D : B {

  void f() const { cout << "D::f "; } // замещает функцию B::f

  void g() { cout << "D::g "; }

};


struct DD : D {

  void f() { cout << "DD::f "; } // не замещает функцию D::f (нет const)

  void g() const { cout << "DD::g "; }

};


Здесь мы описали небольшую иерархию классов с одной виртуальной функцией f(). Мы можем попробовать использовать ее. В частности, можем попробовать вызвать функцию f() и невиртуальную функцию g(), не знающую конкретного типа объекта, который она должна вывести на печать, за исключением того, что он относится либо к классу B, либо к классу, производному от класса B.


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++. Мы не рекомендуем вам становиться крючкотвором (т.е. вникать в мельчайшие детали языковых определений) — быть программистом (разработчиком программного обеспечения, инженером, пользователем, назовите как хотите) намного увлекательнее и полезнее для общества. 

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже