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

Ошибка: ying_yang.print( cout ) -- неоднозначно, одна из

Bear::print( ostream& )

Endangered::print( ostream&, int )

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

В случае одиночного наследования указатель, ссылка или объект производного класса при необходимости автоматически преобразуются в указатель, ссылку или объект базового класса, которому открыто наследует производный. Это остается верным и для множественного наследования. Так, указатель, ссылку или сам объект класса Panda можно преобразовать в указатель, ссылку или объект ZooAnimal, Bear или Endangered:

extern void display( const Bear& );

extern void highlight( const Endangered& );

Panda ying_yang;

display( ying_yang ); // i?aaeeuii

highlight( ying_yang ); // i?aaeeuii

extern ostream&

operator( ostream&, const ZooAnimal& );

cout ying_yang endl; // правильно

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

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()

Обработка виртуального деструктора выполняется правильно независимо от типа указателя, через который мы уничтожаем объект. Например, во всех четырех инструкциях порядок вызова деструкторов один и тот же – обратный порядку вызова конструкторов: