Если бы класс QueryResult объявлял статический член, его определение находилось бы вне области видимости класса TextQuery. Например, статический член класса QueryResult был бы определен как-то так:
// определение статического члена типа int класса QueryResult
// вложенного в класс TextQuery
int TextQuery::QueryResult::static_mem = 1024;
Во вложенном классе выполняются обычные правила поиска имен (см. раздел 7.4.1). Конечно, поскольку вложенный класс — это вложенная область видимости, для поиска у него есть дополнительные области видимости в содержащем классе. Такое вложение областей видимости объясняет, почему переменная-член line_no не определялась во вложенной версии класса QueryResult. Первоначальный класс QueryResult определял этот член для того, чтобы его собственные члены могли избежать необходимости записи TextQuery::line_no. После вложения определения класса результатов в класс TextQuery такое определение типа больше не нужно. Вложенный класс QueryResult может обратиться к переменной line_no без указания, что она определена в классе TextQuery.
Как уже упоминалось, вложенный класс — это тип-член содержащего его класса. Члены содержащего класса могут использовать имена вложенного класса таким же образом, как и любой другой тип-член. Поскольку класс QueryResult вложен в класс TextQuery, функция-член query() класса TextQuery может обращаться к имени QueryResult непосредственно:
// тип возвращаемого значения должен указать, что класс QueryResult
// теперь вложенный
TextQuery::QueryResult
TextQuery::query(const string &sought) const {
// если искомое значение не найдено, возвратить указатель на этот
// набор
static shared_ptr<set<line_no>> nodata(new set<line_no>);
// во избежания добавления слов к wm использовать поиск, а не
// индексирование!
auto loc = wm.find(sought);
if (loc == wm.end())
return QueryResult(sought, nodata, file); // не найдено
else
return QueryResult(sought, loc->second, file);
}
Как обычно, тип возвращаемого значения не находится в области видимости класса (см. раздел 7.4), поэтому сразу было обращено внимание на то, что функция возвращает значение типа TextQuery::QueryResult. Но в теле функции к типу QueryResult можно обращаться непосредственно, как это сделано в операторах return.
Несмотря на то что вложенный класс определяется в пределах содержащего его класса, важно понимать, что никакой связи между объектами содержащего класса и объектами его вложенного класса (классов) нет. Объект вложенного типа только содержит члены, определенные во вложенном типе. Точно так же у объекта содержащего класса есть только те члены, которые определяются содержащим классом. Он не содержит переменные-члены любых вложенных классов.
Конкретней, второй оператор return в функции-члене TextQuery::query() использует переменные-члены объекта класса TextQuery, для которого была выполнена функция query(), инициализирующая объект класса QueryResult:
return QueryResult(sought, loc->second, file);
Эти члены используются для создания возвращаемого объекта класса QueryResult, поскольку он не содержит члены содержащего его класса.
Упражнение 19.20. Вложите собственный класс QueryResult в класс TextQuery и повторно запустите написанную в разделе 12.3.2 программу, использующую класс TextQuery.
19.6. Класс объединения, экономящий место
Класс объединения (union) — это специальный вид класса. У него может быть несколько переменных-членов, но в любой момент времени значение может быть только у одного из членов. Когда присваивается значение одному из членов класса объединения, все остальные члены становятся неопределенными. Объем хранилища, резервируемого для объединения, достаточен для содержания наибольшей переменной-члена. Подобно любому классу, класс объединения определяет новый тип.