В следующем примере, почему я должен явно использовать оператор b->A::DoSomething()
, а не только b->DoSomething()
?
Не должно быть определения перегрузки компилятора, из какого метода я говорю?
Я использую Microsoft VS 2005. (Примечание: использование виртуального не помогает в этом случае.)
class A
{
public:
int DoSomething() {return 0;};
};
class B : public A
{
public:
int DoSomething(int x) {return 1;};
};
int main()
{
B* b = new B();
b->A::DoSomething(); //Why this?
//b->DoSomething(); //Why not this? (Gives compiler error.)
delete b;
return 0;
}
Ответ 1
Две "перегрузки" не совпадают. По умолчанию компилятор учитывает наименьшую возможную область имен до тех пор, пока не найдет совпадение имен. Последовательность аргументов выполняется позже. В вашем случае это означает, что компилятор видит B::DoSomething
. Затем он пытается сопоставить список аргументов, который не работает.
Одним из решений было бы вывести перегрузку из A
в область B
:
class B : public A {
public:
using A::DoSomething;
// …
}
Ответ 2
Разрешение перегрузки является одной из самых уродливых частей С++
В основном компилятор находит совпадение имени "DoSomething (int)" в области B, видит, что параметры не совпадают, и останавливается с ошибкой.
Его можно преодолеть, используя A:: DoSomething в классе B
class A
{
public:
int DoSomething() {return 0;}
};
class B : public A
{
public:
using A::DoSomething;
int DoSomething(int x) {return 1;}
};
int main(int argc, char** argv)
{
B* b = new B();
// b->A::DoSomething(); // still works, but...
b->DoSomething(); // works now too
delete b;
return 0;
}
Ответ 3
Нет, это поведение присутствует, чтобы гарантировать, что вы не поймаете наследование от далеких базовых классов по ошибке.
Чтобы обойти это, вам нужно сообщить компилятору, какой метод вы хотите вызвать, разместив A:: DoSomething в классе B.
См. эту статью для быстрого и легкого обзора этого поведения.
Ответ 4
Наличие метода в производном классе скрывает все методы с тем же именем (независимо от параметров) в базовых классах. Это делается для того, чтобы избежать таких проблем:
class A {} ;
class B :public A
{
void DoSomething(long) {...}
}
B b;
b.DoSomething(1); // calls B::DoSomething((long)1));
чем позже кто-то изменяет класс A:
class A
{
void DoSomething(int ) {...}
}
теперь внезапно:
B b;
b.DoSomething(1); // calls A::DoSomething(1);
Другими словами, если это не сработало так, то несвязанное изменение в классе, который вы не контролируете (A), может бесшумно повлиять на работу вашего кода.
Ответ 5
Это как-то связано с тем, как работает разрешение имен. В основном мы сначала находим область действия, из которой приходит имя, а затем мы собираем все перегрузки для этого имени в этой области. Тем не менее, область действия в вашем случае - класс B, а в классе B, B:: DoSomething скрывает A:: DOSomething:
3.3.7 Скрытие имени [basic.scope.hiding]
... [надрез]...
3 В определении функции участника объявление локального имени скрывается объявление члена одноименного класса; видеть basic.scope.class. Объявление члена в производном классе (class.derived) скрывает объявление члена базового класса одно и то же имя; см. class.member.lookup.
Из-за скрытия имени A:: DoSomething даже не рассматривается для разрешения перегрузки
Ответ 6
Когда вы определяете функцию в производном классе, тогда она скрывает все функции с этим именем в базовом классе. Если функция базового класса является виртуальной и имеет совместимую сигнатуру, функция производного класса также переопределяет базовую функцию класса. Однако это не влияет на видимость.
Вы можете сделать функцию базового класса видимой с помощью объявления using:
class B : public A
{
public:
int DoSomething(int x) {return 1;};
using A::DoSomething;
};
Ответ 7
Это не перегрузка! Это СКРЫТИЕ!
Ответ 8
При поиске дерева наследования для используемой функции С++ использует имя без аргументов, как только оно обнаруживает какое-либо определение, которое оно останавливает, затем анализирует аргументы. В приведенном примере он останавливается в классе B. Чтобы иметь возможность делать то, что вам нужно, класс B должен быть определен следующим образом:
class B : public A
{
public:
using A::DoSomething;
int DoSomething(int x) {return 1;};
};
Ответ 9
Функция скрыта функцией с тем же именем в подклассе (но с другой подписью). Вы можете отобразить его, используя оператор using, как при использовании A:: DoSomething();