В следующем примере, почему я должен явно использовать оператор b->A::DoSomething(), а не просто b->DoSomething()?
Разве разрешение перегрузки компилятора не должно определять, о каком методе я говорю?
Я использую Microsoft VS 2005. (Примечание: использование virtual в этом случае не помогает.)
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;
}





Две «перегрузки» не относятся к одной области. По умолчанию компилятор рассматривает только наименьшую возможную область имени, пока не найдет совпадение имени. Сопоставление аргументов выполняется после. В вашем случае это означает, что компилятор видит B::DoSomething. Затем он пытается сопоставить список аргументов, что терпит неудачу.
Одним из решений было бы перенести перегрузку из A в область B:
class B : public A {
public:
using A::DoSomething;
// …
}
Фактически, если они не находятся в одной области, они также не называются перегрузками.
@Chubsdad: Вот почему я заключил это слово в кавычки. И как только метод помещается в ту же область видимости, он становится перегрузкой.
Разрешение перегрузки - одна из самых уродливых частей C++.
Обычно компилятор находит совпадение имени «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;
}
Когда вы определяете функцию в производном классе, она скрывает все функции с этим именем в базовом классе. Если функция базового класса виртуальная и имеет совместимую сигнатуру, тогда функция производного класса также переопределяет функцию базового класса. Однако это не влияет на видимость.
Вы можете сделать функцию базового класса видимой с помощью объявления using:
class B : public A
{
public:
int DoSomething(int x) {return 1;};
using A::DoSomething;
};
При поиске в дереве наследования функции, которую следует использовать, C++ использует имя без аргументов, как только он обнаруживает какое-либо определение, он останавливается, а затем проверяет аргументы. В приведенном примере он останавливается в классе B. Чтобы иметь возможность делать то, что вам нужно, класс B должен быть определен следующим образом:
class B : public A
{
public:
using A::DoSomething;
int DoSomething(int x) {return 1;};
};
Функция скрыта функцией с тем же именем в подклассе (но с другой подписью). Вы можете отобразить его, используя оператор using, как в случае использования A :: DoSomething ();
Нет, это поведение присутствует, чтобы гарантировать, что вы не попадете в ловушку наследования от удаленных базовых классов по ошибке.
Чтобы обойти это, вам нужно сообщить компилятору, какой метод вы хотите вызвать, поместив using A :: DoSomething в класс B.
См. Эта статья для быстрого и легкого обзора этого поведения.
Это как-то связано с тем, как работает разрешение имен. По сути, мы сначала находим область, из которой происходит имя, а затем собираем все перегрузки для этого имени в этой области. Однако в вашем случае область видимости - это класс B, а в классе B - B :: DoSomething прячется A :: DOSomething:
3.3.7 Скрытие имени [basic.scope.hiding]
... [отрывок] ...
3 В определении функции-члена объявление локального имени скрывает объявление члена класса с таким же именем; видеть basic.scope.class. Объявление члена в производном классе (класс. производный) скрывает объявление члена базового класса то же имя; см. class.member.lookup.
Из-за сокрытия имени A :: DoSomething даже не рассматривается для разрешения перегрузки.
Наличие актуальной ссылки на спецификацию - это хорошо.
Это не перегрузка! Это СКРЫВАЕТСЯ!
Наличие метода в производном классе скрывает все методы с тем же именем (независимо от параметров) в базовых классах. Это сделано, чтобы избежать таких проблем:
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), могли бы незаметно повлиять на работу вашего кода.
Должен сказать, очень хороший момент. В большинстве ответов, которые я видел, говорилось о том, почему возникает ошибка и как ее избежать, но ваша точка зрения показывает, почему эта функция важна и полезна в сценарии реального мира !!
Я пытаюсь вызвать DoSomething () в классе A, используя указатель на класс B.