Не чистые виртуальные функции с параметрами плохая практика?

StackOverflow https://stackoverflow.com/questions/260380

  •  06-07-2019
  •  | 
  •  

Вопрос

У меня есть базовый класс с необязательной виртуальной функцией

class Base {
    virtual void OnlyImplementThisSometimes(int x) {}
};

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

class Base {
    virtual void OnlyImplementThisSometimes(int x) 
    {
        x = 0;
    }
};

У меня также есть проблема в том, что если я не буду осторожен, подкласс, который я создаю, может реализовать неправильную функцию, а затем я не замечаю из-за перегрузки: например,

class Derived : public Base {
    void OnlyImplementThisSometimes(int x, int y) { // some code }
};

Derived d;
Base *b = dynamic_cast<Base *>(&d);
b->OnlyImplementThisSometimes(x); // calls the method in the base class

Метод базового класса был вызван, потому что я реализовал производную функцию с помощью " int y " но нет предупреждения об этом. Это обычные подводные камни в C ++ или я неправильно понял виртуальные функции?

Это было полезно?

Решение

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

virtual void OnlyImplementThisSometimes(int ) { }

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

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

Мы определяем макрос _unused как:

#define _unused(x) ((void)x)

Затем определите функцию как:

virtual void OnlyImplementThisSometimes(int x) { _unused( x );}

Это не только удерживает компилятор от жалоб, но и делает очевидным для любого, кто поддерживает код, о котором вы не забыли, x - вы намеренно его игнорируете.

Зачем определять его в базовом классе? Если базовый класс не будет использовать этот метод, просто определите его как виртуальный метод в производном классе.

Или реализация по умолчанию может вызвать исключение

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

В дополнение к простому опусканию имени переменной во многих компиляторах вы можете сообщить компилятору, что вы знаете, что он не используется, и ВЫКЛЮЧАЕТЕ, выполняя это

int func(int x)
{
   (void) x;
}

Это довольно часто встречается в моем коде. Например, у меня есть классы, которые предназначены для однопоточных операций и многопоточных. Есть много общих процедур и данных. Я положил все это в базовый класс (который также имеет пару чисто виртуальных).

Я реализую две пустые виртуальные функции в базовом классе: Init () и Cleanup (). Однопоточный производный класс не влияет на них, а многопоточный.

У меня есть фабричная функция, создающая подходящий производный класс и возвращающая указатель. Код клиента знает только о типе базового класса и вызывает Init () и Cleanup (). Оба сценария делают правильные вещи.

Конечно, могут быть и другие хорошие предложения о том, как это сделать, но эта идиома хорошо работает для большей части моего кода.

Это не плохая практика, и это распространенная идиома для определения частей класса, которые являются необязательными для реализации.

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

class mouse_listener{
public:
    virtual ~mouse_listener() {}

    virtual void button_down(mouse_button a_Button) {}
    virtual void button_up(mouse_button a_Button) {}
    virtual void scroll_wheel(mouse_scroll a_Scroll) {}
    virtual void mouse_move_abs(math::point a_Position) {}
    virtual void mouse_move_rel(math::point a_Position) {}
};

Кстати, если вы знаете свой базовый класс, вам никогда не нужно делать динамические up -касты, то есть от производных к базовым.

Base *b = &d;

Будет так же хорошо, вместо этого следует использовать dynamic_cast<> при понижении рейтинга, то есть от базы к производной:

if((Derived *d = dynamic_cast<Derived *>(b)) != 0)
{
  // use d
}

(И, конечно, в случае понижения рейтинга, static_cast<> также будет работать.)

Попробуйте это:

class Base {
    virtual void OnlyImplementThisSometimes(int x) = 0;
};

Прошло много времени с тех пор, как я делал подобные вещи, но я верю, что именно так вы объявляете виртуальную функцию.

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

Самый простой ответ на этот вопрос показан ниже:

class Base {
    virtual void OnlyImplementThisSometimes(int x) { x;}
};

Простая ссылка на переменную, которая абсолютно ничего не делает, удалит все предупреждения (во всяком случае, из VC ++ на самом высоком уровне).

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