Выбрать главу

void Panda::mumble()

{

dance( Bear::macarena );

// ...

}

то ближайшей будет локальная область видимости функции-члена mumble(). Если объявление dance в ней имеется, то разрешение имени на этом благополучно завершится. В противном случае поиск будет продолжен в объемлющих областях видимости.

В случае множественного наследования имитируется одновременный просмотр всех поддеревьев наследования – в нашем случае это класс Endangered и поддерево Bear/ZooAnimal. Если объявление обнаружено только в поддереве одного из базовых классов, то разрешение имени заканчивается успешно, как, например, при таком вызове dance():

// правильно: Bear::dance()

yin_yang.dance( Bear::macarena );

Если же объявление найдено в двух или более поддеревьях, то обращение считается неоднозначным и компилятор выдает сообщение об ошибке. Так будет при неквалифицированном обращении к print():

int main()

{

// ошибка: неоднозначность: одна из

// Bear::print( ostream& ) const

// Endangered::print( ostream& ) const

Panda yin_yang;

yin_yang.print( cout );

}

На уровне программы в целом для разрешения неоднозначности достаточно явно квалифицировать имя нужной функции-члена с помощью оператора разрешения области видимости:

int main()

{

// правильно, но не лучшее решение

Panda yin_yang;

yin_yang.Bear::print( cout );

}

Предложенный способ неэффективен: теперь пользователь вынужден решать, каково правильное поведение класса Panda; однако лучше, если такого рода ответственность примет на себя проектировщик и класс Panda сам устранит все неоднозначности, свойственные его иерархии наследования. Простейший способ добиться этого – задать квалификацию уже в определении экземпляра в производном классе, указав тем самым требуемое поведение:

inline void Panda::highlight() {

Endangered::highlight();

}

inline ostream&

Panda::print( ostream &os ) const

{

Bear::print( os );

Endangered::print( os );

return os;

}

Поскольку успешная компиляция производного класса, наследующего нескольким базовым, не гарантирует отсутствия скрытых неоднозначностей, мы рекомендуем при тестировании вызывать все функции-члены, даже самые тривиальные.

Упражнение 18.9

Дана следующая иерархия классов:

class Base1 {

public:

// ...

protected:

int ival;

double dval;

char cval;

// ...

private:

int *id;

// ...

};

class Base2 {

public:

// ...

protected:

float fval;

// ...

private:

double dval;

// ...

};

class Derived : public Base1 {

public:

// ...

protected:

string sval;

double dval;

// ...

};

class MI : public Derived, public Base2 {

public:

// ...

protected:

int *ival;

complexdouble cval;

// ...

};

и структура функции-члена MI::foo():

int ival;

double dval;

void MI::

foo( double dval )

{

int id;

// ...

}

(a)Какие члены видны в классе MI? Есть ли среди них такие, которые видны в нескольких базовых?

(b)Какие члены видны в MI::foo()?

Упражнение 18.10

Пользуясь иерархией классов из упражнения 18.9, укажите, какие из следующих присваиваний недопустимы внутри функции-члена MI::bar():

void MI::

bar()

{

int sval;

// вопрос упражнения относится к коду, начинающемуся с этого места ...

}

(a) dval = 3.14159; (d) fval = 0;

(b) cval = 'a'; (e) sval = *ival;

(c) id = 1;

Упражнение 18.11

Даны иерархия классов из упражнения 18.9 и скелет функции-члена MI::foobar():

int id;

void MI::

foobar( float cval )

{

int dval;

// вопросы упражнения относятся к коду, начинающемуся с этого места ...

}

(a)Присвойте локальной переменной dval сумму значений члена dval класса Base1 и члена dval класса Derived.

(b)Присвойте вещественную часть члена cval класса MI члену fval класса Base2.

(c)Присвойте значение члена cval класса Base1 первому символу члена sval класса Derived.

Упражнение 18.12

Дана следующая иерархия классов, в которых имеются функции-члены print():