Бьерн Страуструп. Язык программирования С++ Второе дополненное издание


 Множественное вхождение базового класса



Pdf көрінісі
бет126/256
Дата11.07.2022
өлшемі2,87 Mb.
#37591
1   ...   122   123   124   125   126   127   128   129   ...   256
Байланысты:
Бьерн Страуструп. Язык программирования С . М Бином, 2011

6.5.1 Множественное вхождение базового класса 
Возможность иметь более одного базового класса влечет за собой возможность неоднократного 
вхождения класса как базового. Допустим, классы task и displayed являются производными класса link, 
тогда в satellite он будет входить дважды: 
class task : public link { 
// link используется для связывания всех 
// задач в список (список диспетчера) 
// 
... 
}; 
class displayed : public link { 
// link используется для связывания всех 
// 
изображаемых объектов (список изображений) 
// 
... 
}; 
Но проблем не возникает. Два различных объекта link используются для различных списков, и эти 
списки не конфликтуют друг с другом. Конечно, без риска неоднозначности нельзя обращаться к членам 
класса link, но как это сделать корректно, показано в следующем разделе. Графически объект satellite 
можно представить так: 
Но можно привести примеры, когда общий базовый класс не должен представляться двумя различными 
объектами (см. $$6.5.3). 
6.5.2 Разрешение неоднозначности 
Естественно, у двух базовых классов могут быть функции-члены с одинаковыми именами
class task { 
// 
... 
virtual debug_info* get_debug(); 
}; 
class displayed { 
// 
... 
virtual debug_info* get_debug(); 
}; 
При использовании класса satellite подобная неоднозначность функций должна быть разрешена


Бьерн Страуструп.
Язык программирования С++ 
 
166 
void f(satellite* sp) 

debug_info* dip = sp->get_debug(); //
ошибка: неоднозначность 
dip = sp->task::get_debug(); // 
нормально 
dip = sp->displayed::get_debug(); // 
нормально 

Однако, явное разрешение неоднозначности хлопотно, поэтому для ее устранения лучше всего 
определить новую функцию в производном классе
class satellite : public task, public derived { 
// ... 
debug_info* 
get_debug() 

debug_info* dip1 = task:get_debug(); 
debug_info* dip2 = displayed::get_debug(); 
return 
dip1->merge(dip2); 

}; 
Тем самым локализуется информация из базовых для satellite классов. Поскольку satellite::get_debug() 
является переопределением функций get_debug() из обоих базовых классов, гарантируется, что именно 
она будет вызываться при всяком обращении к get_debug() для объекта типа satellite. 
Транслятор выявляет коллизии имен, возникающие при определении одного и того же имени в более, 
чем одном базовом классе. Поэтому программисту не надо указывать какое именно имя используется, 
кроме случая, когда его использование действительно неоднозначно. Как правило использование 
базовых классов не приводит к коллизии имен. В большинстве случаев, даже если имена совпадают, 
коллизия не возникает, поскольку имена не используются непосредственно для объектов производного 
класса. 
Аналогичная проблема, когда в двух классах есть функции с одним именем, но разным назначением, 
обсуждается в $$13.8 на примере функции draw() для классов Window и Cowboy. 
Если неоднозначности не возникает, излишне указывать имя базового класса при явном обращении к 
его члену. В частности, если множественное наследование не используется, вполне достаточно 
использовать обозначение типа "где-то в базовом классе". Это позволяет программисту не запоминать 
имя прямого базового класса и спасает его от ошибок (впрочем, редких), возникающих при перестройке 
иерархии классов. Например, в функции из $$6.2.5 
void manager::print() 

employee::print(); 
// 
... 

предполагается, что employee - прямой базовый класс для manager. Результат этой функции не 
изменится, если employee окажется косвенным базовым классом для manager, а в прямом базовом 
классе функции print() нет. Однако, кто-то мог бы следующим образом перестроить классы: 
class employee { 
// 
... 
virtual void print(); 
}; 
class foreman : public employee { 
// 
... 
void 
print(); 
}; 
class manager : public foreman { 
// 
... 
void 
print(); 


Бьерн Страуструп.
Язык программирования С++ 
 
167 
}; 
Теперь функция foreman::print() не будет вызываться, хотя почти наверняка предполагался вызов 
именно этой функции. С помощью небольшой хитрости можно преодолеть эту трудность: 
class foreman : public employee { 
typedef employee inherited; 
// 
... 
void 
print(); 
}; 
class manager : public foreman { 
typedef foreman inherited; 
// 
... 
void 
print(); 
}; 
void manager::print() 

inherited::print(); 
// 
... 

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


Достарыңызбен бөлісу:
1   ...   122   123   124   125   126   127   128   129   ...   256




©emirsaba.org 2024
әкімшілігінің қараңыз

    Басты бет