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 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК