18.3.3. Область видимости класса при множественном наследовании
При одиночном наследовании область видимости производного класса вкладывается в пределы его прямых и косвенных базовых классов (см. раздел 15.6). Поиск имен осуществляется по всей иерархии наследования. Имена, определенные в производном классе, скрывают совпадающие имена в базовом классе.
При множественном наследовании поиск осуществляется одновременно во всех прямых базовых классах. Если имя находится в нескольких базовых классах, происходит ошибка неоднозначности.
В рассматриваемом примере, если имя используется через указатель, ссылку или объект класса Panda, деревья иерархии Endangered и Bear/ZooAnimal исследуются параллельно. Если имя находится в нескольких иерархиях, то возникнет неоднозначность. Для класса вполне допустимо наследовать несколько членов с тем же именем. Но если это имя необходимо использовать, следует указать, какая именно версия имеется в виду.
Когда у класса есть несколько базовых классов, производный класс вполне может унаследовать одноименный член от двух и более своих базовых классов. При использовании этого имени без уточнения класса происходит неоднозначность.
Например, если классы ZooAnimal и Endangered определяют функцию-член max_weight(), а класс Panda не определяет ее, то следующий вызов ошибочен:
double d = ying_yang.max_weight();
В результате наследования класс Panda получает две функции-члена max_weight(), что совершенно допустимо. Наследование создает потенциальную неоднозначность. Ее вполне можно избежать, если объект Panda не будет вызывать функцию-член max_weight(). Ошибки также можно избежать, если явно указать требуемую версию функции: ZooAnimal::max_weight() или Endangered::max_weight(). Ошибка неоднозначности произойдет только при попытке использования функции без уточнения.
Неоднозначность двойного наследования функции-члена max_weight вполне очевидна и логична. Удивительно узнать то, что ошибка произошла бы, даже если у двух наследованных функций были разные списки параметров. Точно так же эта ошибка произошла бы даже в случае, если бы функция max_weight() была закрытой в одном классе и открытой или защищенной в другом. И наконец, если бы функция max_weight() была определена в классе Bear, а не в классе ZooAnimal, то вызов все равно был бы ошибочен.
Как обычно, поиск имени осуществляется под контролем соответствия типов (см. раздел 6.4.1). Когда компилятор находит имя функции max_weight() в двух разных областях видимости, он оповещает об ошибке неоднозначности.
Проще всего избежать потенциальных неоднозначностей, определив версию такой функции в производном классе. Например, снабдив класс Panda функцией max_weight(), можно решить все проблемы:
double Panda::max_weight() const {
return std::max(ZooAnimal::max_weight(),
Endangered::max_weight());
}
Упражнения раздела 18.3.3
Упражнение 18.26. С учетом иерархии кода для упражнений объясните, почему ошибочен следующий вызов функции print()? Исправьте структуру MI так, чтобы позволить этот вызов.
MI mi;
mi.print(42);
Упражнение 18.27. С учетом иерархии кода для упражнений и того, что в структуру MI добавлена приведенная ниже функция foo(), ответьте на следующие вопросы:
int ival;
double dval;
void MI::foo(double cval) {
int dval;
// варианты вопросов упражнения располагаются здесь ...
}
(a) Перечислите все имена, видимые из функции MI::foo().
(b) Видимы ли какие-нибудь имена из больше чем одного базового класса?
(c) Присвойте локальному экземпляру переменной dval сумму переменных-членов dval объектов классов Base1 и Derived.
(d) Присвойте значение последнего элемента вектора MI::dvec переменной-члену Base2::fval.
(e) Присвойте переменной-члену cval класса Base1 первый символ строки sval класса Derived.
Код для упражнений раздела 18.3.3
struct Base1 {
void print(int) const; // по умолчанию открыты
protected:
int ival;
double dval;
char cval;
private:
int *id;
};
struct Base2 {
void print(double) const; // по умолчанию открыты
protected:
double fval;
private:
double dval;
};
struct Derived : public Base1 {
void print(std::string) const; // по умолчанию открыты
protected:
std::string sval;
double dval;
};
struct MI : public Derived, public Base2 {
void print(std::vector<double>); // по умолчанию открыты
protected:
int *ival;
std::vector<double> dvec;
};
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК