17.7.1. Определение класса UserQuery
17.7.1. Определение класса UserQuery
Объект класса UserQuery можно инициализировать указателем на вектор строк, представляющий запрос пользователя, или передать ему адрес этого вектора позже, с помощью функции-члена query(). Это позволяет использовать один объект для нескольких запросов. Фактическое построение иерархии классов Query выполняется функцией eval_query():
// определить объект, не имея запроса пользователя
UserQuery user_query;
string text;
vectorstring query_text;
// обработать запросы пользователя
do {
while( cin text )
query_text.push_back( text );
// передать запрос объекту UserQuery
user_query.query( &query_text );
// вычислить результат запроса и вернуть
// корень иерархии Query*
Query *query = user_query.eval_query();
}
while ( /* пользователь продолжает формулировать запросы */ );
Вот определение нашего класса UserQuery:
#ifndef USER_QUERY_H
#define USER_QUERY_H
#include string
#include vector
#include map
#include stack
typedef pairshort,short location;
typedef vectorlocation,allocator loc;
#include quot;Query.h"t;
class UserQuery {
public:
UserQuery( vector string,allocator *pquery = 0 )
: _query( pquery ), _eval( 0 ), _paren( 0 ) {}
Query *eval_query(); // строит иерархию
void query( vector string,allocator *pq );
void displayQuery();
static void word_map( mapstring,loc*,lessstring,allocator *pwm ) {
if ( !_word_map ) _word_map = pwm;
}
private:
enum QueryType { WORD = 1, AND, OR, NOT, RPAREN, LPAREN };
QueryType evalQueryString( const string &query );
void evalWord( const string &query );
void evalAnd();
void evalOr();
void evalNot();
void evalRParen();
bool integrity_check();
int _paren;
Query *_eval;
vectorstring *_query;
stackQuery*, vectorQuery* _query_stack;
stackQuery*, vectorQuery* _current_op;
static short _lparenOn, _rparenOn;
static mapstring,loc*,lessstring,allocator *_word_map;
};
#endif
Обратите внимание, что два объявленных нами стека содержат указатели на объекты типа Query, а не сами объекты. Хотя правильное поведение обеспечивается обеими реализациями, хранение объектов значительно менее эффективно, поскольку каждый объект (и его операнды) должен быть почленно скопирован в стек (напомним, что операнды копируются виртуальной функцией clone()) только для того, чтобы вскоре быть уничтоженным. Если мы не собираемся модифицировать объекты, помещаемые в контейнер, то хранение указателей на них намного эффективнее.
Ниже показаны реализации различных встроенных операций eval. Операции evalAnd() и evalOr() выполняют следующие шаги. Сначала объект извлекается из стека _query_stack (напомним, что для класса stack, определенного в стандартной библиотеке, это требует двух операций: top() для получения элемента и pop() для удаления его из стека). Затем из хипа выделяется память для объекта класса AndQuery или OrQuery, и указатель на него передается объекту, извлеченному из стека. Каждая операция передает объекту AndQuery или OrQuery счетчики левых или правых скобок, необходимые ему для вывода своего содержимого. И наконец неполный оператор помещается в стек _current_op:
inline void
UserQuery::
evalAnd()
{
Query *pop = _query_stack.top(); _query_stack.pop();
AndQuery *pq = new AndQuery( pop );
if ( _lparenOn )
{ pq-lparen( _lparenOn ); _lparenOn = 0; }
if ( _rparenOn )
{ pq-rparen( _rparenOn ); _rparenOn = 0; }
_current_op.push( pq );
}
inline void
UserQuery::
evalOr()
{
Query *pop = _query_stack.top(); _query_stack.pop();
OrQuery *pq = new OrQuery( pop );
if ( _lparenOn )
{ pq-lparen( _lparenOn ); _lparenOn = 0; }
if ( _rparenOn )
{ pq-rparen( _rparenOn ); _rparenOn = 0; }
_current_op.push( pq );
}
Операция evalNot() работает следующим образом. В хипе создается новый объект класса NotQuery, которому передаются счетчики левых и правых скобок для правильного отображения содержимого. Затем неполный оператор помещается в стек _current_op:
inline void
UserQuery::
evalNot()
{
NotQuery *pq = new NotQuery;
if ( _lparenOn )
{ pq-lparen( _lparenOn ); _lparenOn = 0; }
if ( _rparenOn )
{ pq-rparen( _rparenOn ); _rparenOn = 0; }
_current_op.push( pq );
}
При обнаружении закрывающей скобки вызывается операция evalRParen(). Если число активных левых скобок больше числа элементов в стеке _current_op, то ничего не происходит. В противном случае выполняются следующие действия. Из стека _query_stack извлекается текущий еще не присоединенный к оператору операнд, а из стека _current_op - текущий неполный оператор. Вызывается виртуальная функция add_op() класса Query, которая их объединяет. И наконец полный оператор помещается в стек _query_stack:
inline void
UserQuery::
evalRParen()
{
if ( _paren _current_op.size() )
{
Query *poperand = _query_stack.top();
_query_stack.pop();
Query *pop = _current_op.top();
_current_op.pop();
pop-add_op( poperand );
_query_stack.push( pop );
}
}
Операция evalWord() выполняет следующие действия. Она ищет указанное слово в отображении _word_map взятых из файла слов на векторы позиций. Если слово найдено, берется его вектор позиций и в хипе посредством конструктора с двумя параметрами создается новый объект NameQuery. В противном случае объект порождается с помощью конструктора с одним параметром. Если число элементов в стеке _current_op меньше либо равно числу встреченных ранее скобок, то нет неполного оператора, ожидающего операнда типа NameQuery, поэтому новый объект помещается в стек _query_stack. Иначе из стека _current_op извлекается неполный оператор, к которому с помощью виртуальной функции add_op() присоединяется операнд NameQuery, после чего ставший полным оператор помещается в стек _query_stack:
inline void
UserQuery::
evalWord( const string &query )
{
NameQuery *pq;
loc *ploc;
if ( ! _word_map-count( query ))
pq = new NameQuery( query );
else {
ploc = ( *_word_map )[ query ];
pq = new NameQuery( query, *ploc );
}
if ( _current_op.size() = _paren )
_query_stack.push( pq );
else {
Query *pop = _current_op.top();
_current_op.pop();
pop-add_op( pq );
_query_stack.push( pop );
}
}
Упражнение 17.21
Напишите деструктор, копирующий конструктор и копирующий оператор присваивания для класса UserQuery.
Упражнение 17.22
Напишите функции print() для класса UserQuery. Обоснуйте свой выбор того, что она выводит.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Имя класса
Имя класса Имя класса должно быть уникальным в пределах пакета, который описывается некоторой совокупностью диаграмм классов (возможно, одной диаграммой). Оно указывается в первой верхней секции прямоугольника. В дополнение к общему правилу наименования элементов
11.2.10. Автоматическое определение методов чтения и установки на уровне класса
11.2.10. Автоматическое определение методов чтения и установки на уровне класса Мы уже рассматривали методы attr_reader, attr_writer и attr_accessor, которые немного упрощают определение методов чтения и установки атрибутов экземпляра. А как быть с атрибутами уровня класса?В Ruby нет
8.7. Определение, является ли класс объекта подклассом другого класса
8.7. Определение, является ли класс объекта подклассом другого класса ПроблемаИмеется два объекта и требуется узнать, имеют ли их классы отношения на уровне базовый класс/производный класс, или они не связаны друг с другом.РешениеИспользуйте оператор dynamic_cast, который
Определение членов класса
Определение членов класса Все члены класса по характеру доступа к ним делятся на четыре категории: закрытые (private), защищенные (protected), открытые (public) и опубликованные (published).Элементы класса, определенные в разделе public, без каких-либо ограничений открыты для доступа извне
Тип класса
Тип класса Любой язык, совместимый с .NET, поддерживает, как минимум, тип класса, который является "краеугольным камнем" объектно-ориентированного программирования (ООП). Класс может состоять из любого числа членов (таких, как свойства, методы и события) и элементов данных
Тип класса в C#
Тип класса в C# Если вы имеете опыт создания объектов в рамках какого-то другого языка программирования, то, несомненно, знаете о роли определений классов. Формально говоря, класс – это определенный пользователем тип (User-Defined Type - UDT), который скомпонован из полей данных
Определение открытого интерфейса класса
Определение открытого интерфейса класса После создания данных внутреннего состояния класса и набора конструкторов следующим шагом должно быть определение деталей открытого интерфейса класса. Этим терминам обозначают множество членов, непосредственно доступных из
Определение типов класса
Определение типов класса Пустые пространства имен не представляют собой большого интереса, поэтому давайте выясним, как в CIL определяется тип класса. Вполне логично, что для этого используется директива .class. Однако эта простая директива может иметь множество
Определение класса символов и преобразование символов
Определение класса символов и преобразование символов Функция Краткое описание isalnum проверка на букву или цифру isalpha проверка на букву isascii проверка на символ из набора кодировки ASCII iscntrl проверка на управляющий символ isdigit проверка на десятичную
13.1. Определение класса
13.1. Определение класса Определение класса состоит из двух частей: заголовка, включающего ключевое слово class, за которым следует имя класса, и тела, заключенного в фигурные скобки. После такого определения должны стоять точка с запятой или список объявлений:class Screen { /* ... */
13.1.5. Объявление и определение класса
13.1.5. Объявление и определение класса О классе говорят, что он определен, как только встретилась скобка, закрывающая его тело. После этого становятся известными все члены класса, а следовательно, и его размер.Можно объявить класс, не определяя его. Например:class Screen; //
16.1. Определение шаблона класса
16.1. Определение шаблона класса Предположим, что нам нужно определить класс, поддерживающий механизм очереди. Очередь - это структура данных для хранения коллекции объектов; они помещаются в конец очереди, а извлекаются из ее начала. Поведение очереди описывают
17.2.1. Определение базового класса
17.2.1. Определение базового класса * Члены Query представляют: множество операций, поддерживаемых всеми производными от него классами запросов. Сюда входят как виртуальные операции, переопределяемые в производных классах, так и невиртуальные, разделяемые всеми производными
17.7. Управляющий класс UserQuery
17.7. Управляющий класс UserQuery Если имеется запрос такого типа:fiery && ( bird || potato )то в нашу задачу входит построение эквивалентной иерархии классов:AndQueryNameQuery( "fiery" )OrQueryNameQuery( "bird" )NameQuery( "potato" )Как лучше всего это сделать? Процедура вычисления ответа на запрос напоминает
Конструкторы класса
Конструкторы класса Для класса CObject определены два конструктора. Первый конструктор используется по умолчанию и не имеет параметров. Именно этот конструктор вызывается конструкторами классов, наследованных от CObject:CObject();Второй конструктор класса CObject называется
Конструктор класса
Конструктор класса Класс CString имеет несколько различных конструкторов, позволяющих создавать строки на основе различных данных.Конструктор класса CString, используемый по умолчанию, не имеет параметров. Он создает пустую строку. В последствии вы можете записать в нее любой