Вопрос

На этот вопрос уже есть ответ здесь:

Учитывая следующий пример, почему я должен явно использовать оператор 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;
    // …
}

Другие советы

Разрешение перегрузки - одна из самых уродливых частей 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;
}

Нет, такое поведение присутствует для того, чтобы гарантировать, что вы не попадетесь на наследовании от удаленных базовых классов по ошибке.

Чтобы обойти это, вам нужно сообщить компилятору, какой метод вы хотите вызвать, поместив a с помощью A::doSomething в класс B.

Видишь эта статья для быстрого и удобного ознакомления с этим поведением.

Наличие метода в производном классе скрывает все методы с одинаковым именем (независимо от параметров) в базовых классах.Это сделано для того, чтобы избежать подобных проблем:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

чем позже кто-то сменит класс А:

class A
{
    void DoSomething(int ) {...}
}

теперь внезапно:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

Другими словами, если бы это не сработало подобным образом, несвязанное изменение в классе, который вы не контролируете (A), могло бы незаметно повлиять на работу вашего кода.

Это как-то связано с тем, как работает разрешение имен.По сути, мы сначала находим область, из которой исходит имя, а затем собираем все перегрузки для этого имени в этой области.Однако областью действия в вашем случае является класс B, а в классе B - B::doSomething скрывает Ответ::Что-то делать:

3.3.7 Скрытие имени [basic.scope.hiding]

...[отрывок]...

3 В определении функции-члена объявление локального имени скрывает объявление члена класса с таким же именем;видишь basic.scope.class.Объявление члена в производном классе (класс.производный) скрывает объявление член базового класса то же имя;видишь класс.член.поиск.

Из-за скрытия имени A ::doSomething даже не рассматривается для разрешения перегрузки

Когда вы определяете функцию в производном классе, она скрывает все функции с таким именем в базовом классе.Если функция базового класса является виртуальной и имеет совместимую сигнатуру, то функция производного класса также переопределяет функцию базового класса.Однако это не влияет на видимость.

Вы можете сделать функцию базового класса видимой с помощью объявления 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();

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top