Программирование >>  Полиморфизм без виртуальных функций в с++ 

1 ... 21 22 23 [ 24 ] 25 26 27 ... 144


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

Я намеренно не предоставил в С++ механизма для явного опроса типа объекта:

Имеющееся в Simula67 предложение INSPECT сознательно не включено в С++. Это сделано для того, чтобы заинтересовать программистов в использовании модульного дизайна за счет применения виртуальных функций [Stroustrup,! 986].

Предложение INSPECT в Simula - это не что иное, как switch по поддерживаемому системой полю типа. Однако я видел в это.м решении много минусов и решил, насколько возможно, придерживаться статического контроля типов и виртуальных функций. В конце концов, механизм идентификации типа во время исполнения был-таки добавлен в С++ (см. раздел 14.2). Но я надеюсь, что он все же окажется менее привлекательным, чем предложение INSPECT.

5.5.7. Модель размещения объекта в памяти

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

class А {

int а; public:

virtual void f();

virtual void g(int);

virtual void h(double);

class В : public A { public: int b;

void g(int); замещает A::g() virtual void m(B*);

class С : public В { public: int c;

void h(double); замещает A::h() virtual void n(C*);

to объект класса С выглядит примерно так, как показано на рис. 3.2.



vtbl:

vptr ..

&А: :f

&В: :g

&С: :h

&В: :m

&А: :n

Рис. 3.2

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

void f(C* р) {

p->g(2);

превращается в

(*(p->vptr[1]))(р,2); /* сгенерированный код */

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

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

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

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



3.5.2. Замещение и поиск подходящей виртуальной функции

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

class Base { public:

virtual void f() ;

virtual void g(int);

class Derived : public Base { public:

void f(); замещает Base::f() void g(char); не замещает Base::g()

Здесь скрыта очевидная ловушка. Невиртуальная функция Derived: : g () никак не связана с виртуальной функцией Base: :g () и скрывает ее. Если вы работаете с компилятором, не выдающим предупреждений по этому поводу, то здесь может возникнуть проблема. Однако обнаружить такую ситуацию компилятору не составляет труда. Cfront 1.0 не давал таких предупреждений, чем вызвал немало нареканий. Уже в версии 2.0 это было исправлено.

Правило точного соответствия типов для замещающих функций позже было ослаблено в отношении типа возвращаемого значения (см. раздел 13.7).

3.5.3. Сокрытие членов базового класса

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

class X { int х; public:

virtual void copy(X* p) { x = p->x; }

class XX : public X {

int XX; public:

virtual void copy(XX* p) { xx = p->xx; X::copy(p); }



1 ... 21 22 23 [ 24 ] 25 26 27 ... 144

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика