Предложенный способ неэффективен: теперь пользователь вынужден решать, каково правильное поведение класса 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():
class Base {
public:
void print( string ) const;
// ...
};
class Derived1 : public Base {
public:
void print( int ) const;
// ...
};
class Derived2 : public Base {
public:
void print( double ) const;
// ...
};
class MI : public Derived1, public Derived2 {
public:
void print( complex ) const;
// ...
};
(a)Почему приведенный фрагмент дает ошибку компиляции?
MI mi;
string dancer( "Nejinsky" );
mi.print( dancer );
(b)Как изменить определение MI, чтобы этот фрагмент компилировался и выполнялся правильно?
18.5. Виртуальное наследование A
По умолчанию наследование в C++ является специальной формой композиции по значению. Когда мы пишем:
class Bear : public ZooAnimal { ... };
каждый объект Bear содержит все нестатические данные-члены подобъекта своего базового класса ZooAnimal, а также нестатические члены, объявленные в самом Bear. Аналогично, если производный класс является базовым для какого-то другого:
class PolarBear : public Bear { ... };