KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Стенли Липпман - Язык программирования C++. Пятое издание

Стенли Липпман - Язык программирования C++. Пятое издание

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Стенли Липпман, "Язык программирования C++. Пятое издание" бесплатно, без регистрации.
Перейти на страницу:

Panda ying_yang("ying_yang");

print(ying_yang); // ошибка: неоднозначность

Поиск на основании типа указателя или ссылки

Как и при одиночном наследовании, статический тип объекта, указателя или ссылки определяет, какие из членов можно использовать. Если используется указатель класса ZooAnimal, для применения будут пригодны только те функции, которые определены в этом классе. Части интерфейса класса Panda, специфические для классов Bear, Panda и Endangered, окажутся недоступны. Аналогично указатель или ссылка на класс Bear применимы только для доступа к членам классов Bear и ZooAnimal, а указатель или ссылка на класс Endangered ограничены лишь членами класса Endangered.

В качестве примера рассмотрим следующие вызовы с учетом того, что эти классы определяют виртуальные функции, перечисленные в табл. 18.1.

Bear *pb = new Panda("ying_yang");

pb->print();     // ok: Panda::print()

pb->cuddle();    // ошибка: не является частью интерфейса Bear

pb->highlight(); // ошибка: не является частью интерфейса Bear

delete pb;       // ok: Panda::~Panda()

Когда объект класса Panda используется при помощи указателя или ссылки на класс Endangered, части объекта класса Panda, специфические для классов Panda и Bear, становятся недоступными.

Endangered *ре = new Panda("ying_yang");

pe->print();     // ok: Panda::print()

pe->toes();      // ошибка: не является частью интерфейса Endangered

pe->cuddle();    // ошибка: не является частью интерфейса Endangered

pe->highlight(); // ok: Panda::highlight()

delete pe;       // ok: Panda::~Panda()


Таблица 18.1. Виртуальные функции иерархии классов ZooAnimal/Endangered

Функция Класс, определяющий собственную версию print() ZooAnimal::ZooAnimal Bear::Bear Endangered::Endangered Panda::Panda highlight Endangered::Endangered Panda::Panda toes Bear::Bear Panda::Panda cuddle Panda::Panda Деструктор ZooAnimal::ZooAnimal Endangered::Endangered Упражнения раздела 18.3.2

Упражнение 18.23. Используя иерархию из упражнения 18.22, а также определенный ниже класс D и c учетом наличия у каждого класса стандартного конструктора, укажите, какие из следующих преобразований недопустимы (если таковые вообще имеются)?

class D : public X, public С { ... };

D *pd = new D;

(a) X *px = pd; (b) A *pa = pd;

(с) B *pb = pd; (d) C *pc = pd;

Упражнение 18.24. Выше представлена последовательность вызовов через указатель на класс Bear, указывающих на объект класса Panda. Объясните каждый вызов, подразумевая, что вместо него используется указатель на класс ZooAnimal, указывающий на объект класса Panda.

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

class D1 : public Base1 { /* ... */ };

class D2 : public Base2 { /* ... */ };

class MI : public D1, public D2 {/* ... */ };

Используя следующие определения, укажите, какая из функций используется при каждом вызове:

Base1 *pb1 = new MI;

Base2 *pb2 = new MI;

D1 *pd1 = new MI;

D2 *pd2 = new MI;

(a) pb1->print(); (b) pd1->print(); (c) pd2->print();

(d) delete pb2;   (e) delete pd1;   (f) delete pd2;

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;

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*