Проблемы с частичным переопределением функций класса в C ++ - PullRequest
6 голосов
/ 13 января 2010

Есть ли проблема с частичным переопределением набора виртуальных функций, определенных базовым классом?

Мой компилятор выдает следующее предупреждение:

overloaded virtual function "MyBaseClass::setValue" is only partially overridden in class "MyDerivedClass".

Классы выглядят так:

class MyBaseClass
{
public:
    virtual void setValue(int);
    virtual void setValue(SpecialType*);
}

class MyDerivedClass : public MyBaseClass
{
public:
    virtual void setValue(int);
}

Самый простой способ избавиться от этого предупреждения - использовать разные имена для базовых функций, но я хотел знать, есть ли веская причина для исправления этого конкретного предупреждения. Я не верю, что это нарушает стандарт C ++. Я предполагаю, что это должно предупредить программиста, что он, возможно, забыл реализовать поведение для всех возможных типов ввода. В нашем случае намеренно исключить некоторые из конкретных типов.

Не хотите ли вообще подавить это предупреждение?

Ответы [ 3 ]

13 голосов
/ 13 января 2010

Переопределение для setValue(int) скрывает setValue(SpecialType*) базового класса (см. C ++ FAQ Lite ), поэтому, если вы попытаетесь вызвать setValue(new SpecialType()), вы получите ошибку.

Этого можно избежать, добавив директиву using к производному классу, который «импортирует» перегрузки из базового класса:

class MyDerivedClass : public MyBaseClass
{
public:
    using MyBaseClass::setValue;
    virtual void setValue(int);
};
6 голосов
/ 13 января 2010

Предупреждение правильное, оно называется «скрытие имени». Переменная типа MyDerivedClass не может вызвать setValue(SpecialType*).


Теперь я собираюсь нагло сорвать чужой блог :

Перегрузка и скрытие имени в C ++

В телефонном разговоре с Брэдом прошлой ночью он рассказал мне о странной проблеме, с которой он столкнулся в своей новой работе на C ++. Конечно, это не имеет большого значения для людей с обширным опытом работы с C ++, но для тех из нас, кто живет в мире управляемого кода, это казалось странным.

В C ++, когда у вас есть класс с перегруженным методом (функция-член, как бы вы его не вызывали), а затем вы расширяете и переопределяете этот метод, вы должны переопределить все перегруженные методы.

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

Например:

class FirstClass
{
public:
        virtual void MethodA (int);
        virtual void MethodA (int, int);
};

void FirstClass::MethodA (int i)
{
    std::cout << "ONE!!\n";
}

void FirstClass::MethodA (int i, int j)
{
     std::cout << "TWO!!\n";
}

Простой класс здесь с двумя методами (или одним перегруженным методом). Вы хотите переопределить двухпараметрическую версию, поэтому продолжите со следующего:

class SecondClass : public FirstClass
{
public:
    void MethodA (int);
};

void SecondClass::MethodA (int i)
{
    std::cout << "THREE!!\n";
}

Теперь, когда вы используете экземпляр SecondClass, большинство программистов на Java или C # могут предположить, что вы можете вызвать:

int main ()
{
    SecondClass a;
    a.MethodA (1);
    a.MethodA (1, 1);
}

Однако второй вызов не будет работать, поскольку двухпараметрический метод A не отображается. Вы можете получить указатель и выполнить приведение к FirstClass, но ваш экземпляр SecondClass не наследует не переопределенные методы напрямую.

0 голосов
/ 13 января 2010

Понятно, что компилятор хочет предупредить вас: вы создали подкласс, который ведет себя по-разному, присваивая ему int, но вы не изменили его поведение, задав ему SpecialType*.

Хотя это может быть намерением, вполне возможно, что измененное поведение также необходимо для других перегруженных виртуальных функций.

Хотелось бы, чтобы компилятор предупреждал меня сильнее, когда япроигнорировал это!Мой переопределенный метод оказался скомпилированным и хорошо работает в моем сценарии, но некоторые другие сценарии действительно пошли не так, потому что перегрузка не была переопределена.

Подумайте дважды, прежде чем отключить это предупреждение!если вы хотите сохранить исходное поведение, просто вызовите родительскую функцию:

class MyDerivedClass : public MyBaseClass {
   virtual void setValue(int);
   // explicit: keep original behavior for SpecialType
   virtual void setValue( SpecialType* p ) { MyBaseClass::setValue(p); } 
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...