|
Программирование >> Полиморфизм без виртуальных функций в с++
такие вызовы завершатся неудачно при работе с некоторыми компиляторами, поддерживаюшими динамическое связывание. Во время курса, который я читал в 1987 г. для EUUG (Европейская группа пользователей UNIX) в Хельсинки, Мартин ОРиордан указал мне, что статические функции-члены - очевидное и полезное обобшение. Возможно, именно тогда эта идея и прозвучала впервые. Мартин в то время работал в ирландской компании Glockenspiel, а позже стал главным архитектором компилятора Microsoft С-ы-. Позже Джонатан Шопиро поддержал эту идею и не дал ей затеряться во время работы над версией 2.0. Статическая функция-член остается членом класса, значит, ее и.мя находится в области действия класса и к нему применимы обычные правила контроля доступа. Например: class task { . . . static task* chain; public: static void schedule(int); ... Объявление статического члена - это всего лишь объявление, так что соот-ветствуюший объект или функция должны иметь где-то в программе единственное определение. Например: task* task::chain = 0; void task::schedule(int p) { /* ... */ } Статическая функция-член не ассоциирована ни с каким конкретным объектом, и для ее вызова не нужен специальный синтаксис для функций-членов. Например: void f{int priority) { . . . task::schedule(priority); ... В некоторых слзаях класс используется просто как область действия, в которую глобальные имена помещаются под видом статических членов, чтобы не засорять глобальное пространство имен. Это является одним из источников концепции пространств имен (см. главу 17). Статические функции-члены оказались среди тех свойств языка, которые были единодушно поддержаны во время дискуссий на семинаре разработчиков ксмпиляторов в Эстес Парк (см. раздел 7.1.2). 13.5. Вложенные классы Как уже отмечалось в разделе 3.12, вложенные классы повторно введены в С-ы- в ARM. Это сделало более регулярными правила областей действия и улучшило средства локализации информации. Теперь стало возможно написать: class String { class Rep { . . . Rep* p; String - это описатель для Rep static int count; . . . public: char& operator[](int i); ... чтобы оставить класс Rep локальным. К сожалению, это приводило к увеличению объема информации, необходимой для объявления классов, следовательно, к увеличению времени компиляции и частоты переко.мпиляций. Слишком много интересной и изменяющейся информации помещалось во вложенные классы. Во многих случаях эта информация была неважна для пользователей такого класса, как String и ее следовало расположить в другом месте, наряду с прочими дета-ля.ми реализации. Тони Хансен предложил разрешить опережающие объявления вложенных классов точно так же, как это делалось для функций-членов и статических членов: файл String.h (интерфейс): class String { class Rep; Rep* p; String - описатель для Rep static int count; ... public: char& operator[](int i); ... файл String.с (реализация): class String::Rep { . . . static int String::count = 1; cliar& String: :operator[] (int i) ( ... Это расширение было принято просто как исправление недочета. Однако технику, которую оно поддерживает, не следует недооценивать. Ведь многие продолжают перегружать заголовочные фа11лы всякой чепухой и страдают оттого, что перекомпиляция занимает много времени. Поэтому так важны любой прием, средство, которые позволяют уменьшить зависимость пользователя от разработчика компилятора. 13.6. Ключевое слово inherited На одном из первых заседаний комитета по стандартизации Дэг Брюк предложил расширение, которым заинтересовались несколько человек [Stroustrup, 1992b]: Многие иерархии классов строятся инкрементно , путем расширения функциональности базового класса за счет производного. Как провило, функция из производного классе вызывоет функцию из бозового, а затем выполняет некоторые дополнительные действия: struct А { virtual void handle{int); }; struct D : A { void handle (int) ; } ,-void D::handle(int i); { A::handle(i); прочие действия Вызов handle () необходимо квалифицировать кок имя класса, чтобы избежоть рекурсивного зоцикливония. С помощью предлогаемого расширения этот пример можно было бы переписать ток: void D::handle(int i); { inherited::handle(i); прочие действия Квалификоцию с помощью ключевого слова inherited допустимо рассматривать как обобщение квалификации именем класса. Тогда решается целый ряд связанных с этим потенциальных проблем, что особенно важно при сопровождении библиотек классов . Я обду.мывал это предложение на ранних стадиях проектирования С++, но предпочел квалификацию именем базового класса, поскольку такой способ мог работать множественным наследованием, а inherited: : - нет. Однако Дэг отметил, что описанные проблемы .можно решить, сочетая обе схе.мы: По большей части при розработке иерархий имеется в виду одиночное наследовоние. Если изменить дерево наследовония ток, что класс D наследует одновременно А и В, то получим: struct А { virtual void handle(int); }; struct В { virtual void handle(int); }; struct D : A, В { void handle(int); ); void D::handle(int i); { A::handle(i); однозначно inherited::handle(i); неоднозначно
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.487
При копировании материалов приветствуются ссылки. |