Не чистые виртуальные функции с параметрами плохая практика? - PullRequest
9 голосов
/ 04 ноября 2008

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

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 ++ или я неправильно понял виртуальные функции?

Ответы [ 10 ]

22 голосов
/ 04 ноября 2008

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

virtual void OnlyImplementThisSometimes(int ) { }

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

11 голосов
/ 04 ноября 2008

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

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

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

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

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

6 голосов
/ 04 ноября 2008

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

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

4 голосов
/ 04 ноября 2008

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

3 голосов
/ 04 ноября 2008

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

int func(int x)
{
   (void) x;
}
2 голосов
/ 04 ноября 2008

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

Base *b = &d;

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

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

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

2 голосов
/ 04 ноября 2008

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

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

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) {}
};
2 голосов
/ 04 ноября 2008

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

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

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

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

0 голосов
/ 05 ноября 2008

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

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

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

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

0 голосов
/ 04 ноября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...