// не объектно-ориентированное решение
union op_type {
// объединение не может содержать объекты классов с
// ассоциированными конструкторами
NotQuery *nq;
OrQuery *oq;
AndQuery *aq;
string *word;
};
enum opTypes {
Not_query=1, O_query, And_query, Name_query
};
class AndQuery {
public:
// ...
private:
/*
* opTypes хранит информацию о фактических типах операндов запроса
* op_type - это сами операнды
*/
op_type _lop, _rop;
opTypes _lop_type, _rop_type;
};
Хранить указатели на объекты можно и с помощью типа void*:
class AndQuery {
public:
// ...
private:
void * _lop, _rop;
opTypes _lop_type, _rop_type;
};
Нам все равно нужен дискриминант, поскольку напрямую использовать объект, адресуемый указателем типа void*, нельзя, равно как невозможно определить тип такого объекта по указателю. (Мы не рекомендуем применять описанное решение в C++, хотя в языке C это весьма распространенный подход.)
Основной недостаток рассмотренных решений состоит в том, что ответственность за определение типа возлагается на программиста. Например, в случае решения, основанного на void*-указателях, операцию eval() для объекта AndQuery можно реализовать так:
void
AndQuery::
eval()
{
// не объектно-ориентированный подход
// ответственность за разрешение типа ложится на программиста
// определить фактический тип левого операнда
switch( _lop_type ) {
case And_query:
AndQuery *paq = static_cast<AndQuery*>(_lop);
paq->eval();
break;
case Or_query:
OrQuery *pqq = static_cast<OrQuery*>(_lop);
poq->eval();
break;
case Not_query:
NotQuery *pnotq = static_cast<NotQuery*>(_lop);
pnotq->eval();
break;
case Name_query:
AndQuery *pnmq = static_cast<NameQuery*>(_lop);
pnmq->eval();
break;