bool
NameQuery::
compare( const Query *pquery )
{
// правильно: защищенный член подобъекта Query
int myMatches = _loc.size();
// правильно: используется открытый метод доступа
int itsMatches = pquery-locations()-size();
return myMatches == itsMatches;
}
Однако проблема заключается в неправильном проектировании. Поскольку _loc - это член базового класса Query, то место compare() среди членов базового, а не производного класса. Во многих случаях подобные проблемы могут быть решены путем переноса некоторой операции в тот класс, где находится недоступный член, как в приведенном примере.
Этот вид ограничения доступа не распространяется на доступ изнутри класса к другим объектам того же класса:
bool
NameQuery::
compare( const NameQuery *pname )
{
int myMatches = _loc.size(); // правильно
int itsMatches = name-_loc.size(); // тоже правильно
return myMatches == itsMatches;
}
Производный класс может напрямую обращаться к защищенным членам базового в других объектах того же класса, что и он сам, равно как и к защищенным и закрытым членам других объектов своего класса.
Рассмотрим инициализацию указателя на базовый Query адресом объекта производного NameQuery:
Query *pb = new NameQuery( "sprite" );
При вызове виртуальной функции, определенной в базовом классе Query, например:
pb-eval(); // вызывается NameQuery::eval()
вызывается функция из NameQuery. За исключением вызова виртуальной функции, объявленной в Query и переопределенной в NameQuery, другого способа напрямую добраться до членов класса NameQuery через указатель pb не существует:
если в Query и NameQuery объявлены некоторые невиртуальные функции-члены с одинаковым именем, то через pb всегда вызывается экземпляр из Query;
если в Query и NameQuery объявлены одноименные члены, то через pb обращение происходит к члену класса Query;
если в NameQuery имеется виртуальная функция, отсутствующая в Query, скажем suffix(), то попытка вызвать ее через pb приводит к ошибке компиляции:
// ошибка: suffix() - не член класса Query
pb-suffix();
Обращение к члену или невиртуальной функции-члену класса NameQuery через pb тоже вызывает ошибку компиляции:
// ошибка: _name - не член класса Query
pb-_name;
Квалификация имени члена в этом случае не помогает:
// ошибка: у класса Query нет базового класса NameQuery
pb-NameQuery::_name;
В C++ с помощью указателя на базовый класс можно работать только с данными и функциями-членами, включая виртуальные, которые объявлены (или унаследованы) в самом этом классе, независимо от того, какой фактический объект адресуется указателем. Объявление функции-члена виртуальной откладывает решение вопроса о том, какой экземпляр функции вызвать, до выяснения (во время выполнения программы) фактического типа объекта, адресуемого pb.
Такой подход может показаться недостаточно гибким, но у него есть два весомых преимущества:
поиск виртуальной функции-члена во время выполнения никогда не закончится неудачно из-за того, что фактический тип класса не существует. В таком случае программа просто не смогла бы откомпилироваться;
механизм виртуализации можно оптимизировать. Часто вызов такой функции оказывается не дороже, чем косвенный вызов функции по указателю (детально этот вопрос рассмотрен в [LIPPMAN96a]).
В базовом классе Query определен статический член _text_file:
static vectorstring *_text_file;
Создается ли при порождении класса NameQuery второй экземпляр _text_file, уникальный именно для него? Нет. Все объекты производного класса ссылаются на тот же самый, единственный разделяемый статический член. Сколько бы ни было производных классов, существует лишь один экземпляр _text_file. Можно обратиться к нему через объект производного класса с помощью синтаксиса доступа:
nameQueryObject._text_file; // правильно
Наконец, если производный класс хочет получить доступ к закрытым членам своего базового класса напрямую, то он должен быть объявлен другом базового:
class Query { friend class NameQuery; public: // ... };
Теперь объект NameQuery может обращаться не только к закрытым членам своего подобъекта, соответствующего базовому классу, но и к закрытым и защищенным членам любых объектов Query.