|
|
Естественно, у двух базовых классов могут быть функции-члены с одинаковыми именами:
class task { // ... virtual debug_info* get_debug(); }; class displayed { // ... virtual debug_info* get_debug(); };
При использовании класса satellite подобная неоднозначность функций должна быть разрешена:
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(); };
Теперь функция 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 наглядным или нет.
| |