extern void display( const Bear& );
extern void display( const Endangered& );
Неквалифицированный вызов display() для объекта класса Panda
Panda ying_yang;
display( ying_yang ); // ошибка: неоднозначность
приводит к ошибке компиляции:
Error: display( ying_yang ) -- ambiguous, one of
display( const Bear& );
display( const Endangered& );
Ошибка: display( ying_yang ) -- неоднозначно, одна из
display( const Bear& );
display( const Endangered& );
Компилятор не может различить два непосредственных базовых класса с точки зрения преобразования производного. Равным образом применимы обе трансформации. (Мы покажем способ разрешения этого конфликта в разделе 18.4.)
Чтобы понять, какое влияние оказывает множественное наследование на механизм виртуальных функций, определим их набор в каждом из непосредственных базовых классов Panda. (Виртуальные функции введены в разделе 17.2 и подробно обсуждались в разделе 17.5.)
class Bear : public ZooAnimal {
public:
virtual ~Bear();
virtual ostream& print( ostream& ) const;
virtual string isA() const;
// ...
};
class Endangered {
public:
virtual ~Endangered();
virtual ostream& print( ostream& ) const;
virtual void highlight() const;
// ...
};
Теперь определим в классе Panda собственный экземпляр print(), собственный деструктор и еще одну виртуальную функцию cuddle():
class Panda : public Bear, public Endangered
{
public:
virtual ~Panda();
virtual ostream& print( ostream& ) const;
virtual void cuddle();
// ...
};
Множество виртуальных функций, которые можно напрямую вызывать для объекта Panda, представлено в табл. 18.1.
Таблица 18.1. Виртуальные функции для класса Panda Имя виртуальной функции | Активный экземпляр |
деструктор | Panda::~Panda() |
print(ostream&) const | Panda::print(ostream&) |
isA() const | Bear::isA() |
highlight() const | Endangered::highlight() |
cuddle() | Panda::cuddle() |
Когда ссылка или указатель на объект Bear или ZooAnimal инициализируется адресом объекта Panda или ему присваивается такой адрес, то части интерфейса, связанные с классами Panda и Endangered, становятся недоступны:
Bear *pb = new Panda;
pb-print( cout ); // i?aaeeuii: Panda::print(ostream&)
pb-isA(); // i?aaeeuii: Bear::isA()
pb-cuddle(); // ioeaea: yoi ia ?anou eioa?oaena Bear
pb-highlight(); // ioeaea: yoi ia ?anou eioa?oaena Bear
delete pb; // правильно: Panda::~Panda()
(Обратите внимание, что если бы объекту класса Panda был присвоен указатель на ZooAnimal, то все показанные выше вызовы разрешались бы так же.)
Аналогично, если ссылка или указатель на объект Endangered инициализируется адресом объекта Panda или ему присваивается такой адрес, то части интерфейса, связанные с классами Panda и Bear, становятся недоступными:
Endangered *pe = new Panda;
pe-print( cout ); // правильно: Panda::print(ostream&)
// ioeaea: yoi ia ?anou eioa?oaena Endangered
pe-cuddle();
pe-highlight(); // правильно: Endangered::highlight()
delete pe; // правильно: Panda::~Panda()
Обработка виртуального деструктора выполняется правильно независимо от типа указателя, через который мы уничтожаем объект. Например, во всех четырех инструкциях порядок вызова деструкторов один и тот же – обратный порядку вызова конструкторов:
// ZooAnimal *pz = new Panda;
delete pz;
// Bear *pb = new Panda;
delete pb;
// Panda *pp = new Panda;
delete pp;