17.5.3. Статический вызов виртуальной функции
17.5.3. Статический вызов виртуальной функции
Вызывая виртуальную функцию с помощью оператора разрешения области видимости класса, мы отменяем механизм виртуализации и разрешаем вызов статически, на этапе компиляции. Предположим, что мы определили виртуальную функцию isA() в базовом и каждом из производных классов иерархии Query:
Query *pquery = new NameQuery( " dumbo" );
// isA() вызывается динамически с помощью механизма виртуализации
// реально будет вызвана NameQuery::isA()
pquery-isA();
// isA вызывается статически во время компиляции
// реально будет вызвана Query::isA
pquery-Query::isA();
Тогда явный вызов Query::isA() разрешается на этапе компиляции в пользу реализации isA() в базовом классе Query, хотя pquery адресует объект NameQuery.
Зачем нужно отменять механизм виртуализации? Как правило, ради эффективности. В теле виртуальной функции производного класса часто необходимо вызвать реализацию из базового, чтобы завершить операцию, расщепленную между базовым и производным классами. К примеру, вполне вероятно, что виртуальная функция display() из Camera выводит некоторую информацию, общую для всех камер, а реализация display() в классе PerspectiveCamera сообщает информацию, специфичную только для перспективных камер. Вместо того чтобы дублировать в ней действия, общие для всех камер, можно вызвать реализацию из класса Camera. Мы точно знаем, какая именно реализация нам нужна, поэтому нет нужды прибегать к механизму виртуализации. Более того, реализация в Camera объявлена встроенной, так что разрешение во время компиляции приводит к подстановке по месту вызова.
Приведем еще один пример, когда отмена механизма виртуализации может оказаться полезной, а заодно познакомимся с неким аспектом чисто виртуальных функций, который начинающим программистам кажется противоречащим интуиции.
Реализации функции print() в классах AndQuery и OrQuery совпадают во всем, кроме литеральной строки, представляющей название оператора. Реализуем только одну функцию, которую можно вызывать из данных классов. Для этого мы снова определим абстрактный базовый BinaryQuery (его наследники - AndQuery и OrQuery). В нем определены два операнда и еще один член типа string для хранения значения оператора. Поскольку это абстрактный класс, объявим print() чисто виртуальной функцией:
class BinaryQuery : public Query {
public:
BinaryQuery( Query *lop, Query *rop, string oper )
: _lop(lop), _rop(rop), _oper(oper) {}
~BinaryQuery() { delete _lop; delete _rop; }
ostream &print( ostream&=cout, ) const = 0;
protected:
Query *_lop;
Query *_rop;
string _oper;
};
Вот как реализована в BinaryQuery функция print(), которая будет вызываться из производных классов AndQuery и OrQuery:
inline ostream&
BinaryQuery::
print( ostream &os ) const
{
if ( _lparen )
print_lparen( _lparen, os );
_lop-print( os );
os _oper ;
_rop-print( os );
if ( _rparen )
print_rparen( _rparen, os );
return os;
}
Похоже, мы попали в парадоксальную ситуацию. С одной стороны, необходимо объявить этот экземпляр print() как чисто виртуальную функцию, чтобы компилятор воспринимал BinaryQuery как абстрактный базовый класс. Тогда в приложении определить независимые объекты BinaryQuery будет невозможно.
С другой стороны, нужно определить в классе BinaryQuery виртуальную функцию print() и уметь вызывать ее через объекты AndQuery и OrQuery.
Но как часто бывает с кажущимися парадоксами, мы не учли одного обстоятельства: чисто виртуальную функцию нельзя вызывать с помощью механизма виртуализации, но можно вызывать статически:
inline ostream&
AndQuery::
print( ostream &os ) const
{
// правильно: подавить механизм виртуализации
// вызвать BinaryQuery::print статически
BinaryQuery::print( os );
}
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Многократный вызов функции connect для сокета UDP
Многократный вызов функции connect для сокета UDP Процесс с присоединенным сокетом UDP может снова вызвать функцию connect Для этого сокета, чтобы:? задать новый IP-адрес и порт;? отсоединить сокет.Первый случай, задание нового собеседника для присоединенного сокета UDP, отличается
Статический вес
Статический вес Определение страниц, получающих недостаточный статический вес. Часть важных страниц может недополучать статический вес. Вместо этого наибольший вес может переходить к непродвигаемым и техническим страницам. Чтобы решить эту проблему, необходимо:?
Объекты имеют статический тип
Объекты имеют статический тип Один из выводов, который можно сделать из трех требований QueryInterfасе , состоит в том, что множество интерфейсов, поддерживаемых объектом, не может изменяться во времени. Спецификация СОМ четко требует, чтобы этот вывод был верен для всех
10.1. PSPICE как статический логический анализатор
10.1. PSPICE как статический логический анализатор Шаг 1 Начертите в редакторе SCHEMATICS схему, изображенную на рис. 10.1. Необходимые компоненты вы найдете в библиотеке EVAL.slb. Редактор для установления метки (out) можно открыть, дважды щелкнув мышью по соответствующему участку
R.5.2.2 Вызов функции
R.5.2.2 Вызов функции Вызов функции является постфиксным выражением, за которым следует, возможно пустой, список выражений в скобках, разделенных запятой. Эти выражения образуют фактические параметры функции. Постфиксное выражение должно иметь тип "функция, возвращающая T",
R.13.4.4 Вызов функции
R.13.4.4 Вызов функции Вызов функции есть конструкция вида:первичное-выражение ( список-выражений opt )Она считается бинарной операцией, в которой первичное-выражение представляет первый операнд, а список-выражений (возможно пустой), - второй операнд. Именем, задающим функцию,
64. Разумно сочетайте статический и динамический полиморфизм
64. Разумно сочетайте статический и динамический полиморфизм РезюмеСтатический и динамический полиморфизм дополняют друг друга. Следует ясно представлять себе их преимущества и недостатки, чтобы использовать каждый из них там, где он дает наилучшие результаты, и
8.15. Вызов виртуальной функции родительского класса
8.15. Вызов виртуальной функции родительского класса ПроблемаТребуется вызвать функцию родительского класса, но она переопределена в производном классе, так что обычный синтаксис p->method() не дает нужного результата.РешениеУкажите полное имя вызываемого метода, включая
Вызов функции с аргументом: фактические аргументы
Вызов функции с аргументом: фактические аргументы Задача в данном случае состоит в том, чтобы присвоить некоторую величину формальному аргументу number. После того как эта переменная получит свое значение, программа сможет выполнить свою задачу. Мы присваиваем
Вызов функции
Вызов функции Вызов функции передает управление и фактические аргументы (если они есть) заданной функции. Синтаксически вызов функции имеет следующий вид:<выражение>([<список выражений>])<Выражение> вычисляется, и его результат интерпретируется как адрес
Вызов функции с переменным числом аргументов
Вызов функции с переменным числом аргументов Для вызова функции с переменным числом аргументов не требуется никаких специальных действий: в вызове функции просто задается то число аргументов, которое нужно. В предварительном объявлении (если оно есть) переменное число
6.8 Вызов Функции
6.8 Вызов Функции Вызов функции, то есть запись выражение(список_выражний), можно проинтерпретировать как бинарную операцию, и операцию вызова можно перегружать так же, как и другие оперции. Список параметров функции operator() вычисляется и прверяется в соответствие с
Статический тип, динамический тип
Статический тип, динамический тип Название последнего свойства предполагает различение "статического типа" и "динамического типа". Тип, который используется при объявлении некоторого элемента, является статическим типом соответствующей ссылки. Если во время выполнения
Статический механизм
Статический механизм Устранить последнее неясности в понимании закрепленного объявления поможет следующее замечание: это чисто статический механизм, не предполагающий никаких изменений объектов в период выполнения. Все ограничения могут быть проверены в период