Параметры виртуальной функции по умолчанию и перегрузка

Этот вопрос относится к общим проблемам, обсуждаемым в этих вопросах:

Могут ли виртуальные функции иметь параметры по умолчанию?

Параметры виртуальных функций по умолчанию

Вот что в настоящее время происходит в c ++ с параметрами по умолчанию для виртуальных функций:

struct Base
{
virtual void foo(int one = 1, int two = 2)
{ cout << "one: " << one << " two: " << two << endl; }
};

struct Derived : public Base
{
virtual void foo(int one = 3, int two = 4)
{ Base::foo(one, two); cout << " derived!" << endl; }
};

int main()
{
Base* b = new Base();
Base* d = new Derived();

Derived* dp = new Derived();

b->foo();
d->foo();
dp->foo();

return 0;
}

выход:

one: 1 two: 2
one: 1 two: 2
derived!
one: 3 two: 4
derived!

Это поведение, которое я хотел бы видеть в параметрах по умолчанию для виртуальной функции c ++:

#include <iostream>

using namespace std;

struct Base
{
virtual void foo () { foo(1, 2); }
virtual void foo (int one) { foo(one, 2); }
virtual void foo(int one, int two)
{ cout << "one: " << one << " two: " << two << endl; }
};

struct Derived : public Base
{
virtual void foo() { foo(3, 4); }
virtual void foo(int one, int two)
{ Base::foo(one, two); cout << " derived!" << endl; }
};

int main()
{
Base* b = new Base();
Base* d = new Derived();

Derived* dp = new Derived();

b->foo();
d->foo();
dp->foo();

return 0;
}

выход:

one: 1 two: 2
one: 3 two: 4
derived!
one: 3 two: 4
derived!

Так что, по сути, если я хочу переопределить параметр по умолчанию в родительском классе, я просто создам новый foo с таким количеством аргументов. Обратите внимание, что производное переопределяет условие без аргументов, но не условие с одним аргументом.
Как примечание, мой текущий проект использует чисто виртуальный базовый класс. У меня часто есть указатели на тип базового класса и тип производного класса. Я хочу, чтобы звонок с любого указателя имел одинаковый результат.

ВОПРОСЫ:

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

Некоторые говорят: «Не используйте параметры по умолчанию для виртуальных функций», но тогда мне нужно будет установить значение по умолчанию в каждом месте, где я вызываю функцию. Кажется, было бы лучше просто написать комментарий, который говорит: «также измените базовый класс» и «также измените производный класс», чем менять везде, где вызывается функция.

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

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

3

Решение

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

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

#include <iostream>

using namespace std;

struct Base
{
void foo(int one = 1, int two = 2) { foo_impl(one, two); }

protected:
virtual void foo_impl(int one, int two)
{ cout << "one: " << one << " two: " << two << endl; }
};

struct Derived : public Base
{
protected:
virtual void foo_impl(int one, int two)
{ Base::foo_impl(one, two); cout << " derived!" << endl; }
};

int main()
{
Base* b = new Base();
Base* d = new Derived();

Derived* dp = new Derived();

b->foo();
d->foo();
dp->foo();

return 0;
}
3

Другие решения

Я думаю, что проблема в «понимании того, что происходит», заключается в том, что значения по умолчанию для аргументов функции решаются во время компиляции — это означает, что, если это не ОЧЕНЬ очевидно и просто, компилятор не будет ЗНАТЬ, на какой класс указывает какой-то указатель, и выиграл не дать правильный аргумент. Другими словами, компилятор будет использовать класс указателя при определении аргументов для вашей функции. Нет абсолютно никакого способа обойти это (кроме «знания, какой класс вы хотите использовать», но тогда использование виртуальных функций довольно бессмысленно).

Решение зависит от того, что вы ДЕЙСТВИТЕЛЬНО хотите сделать, но очевидный ответ — НЕ использовать аргументы по умолчанию. Другое решение, как и у вас, это какой-то косвенный вызов функции, где функция «без аргументов» отличается от функций с одним и двумя аргументами.

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

2

Прежде всего, вы не используете параметры по умолчанию в вашем примере. Таким образом, проблема, связанная с обычными параметрами по умолчанию, не относится к вашему коду. В вашем коде подклассы Можно переопределить параметры по умолчанию, которые будут работать, как ожидается, независимо от того, если Derived используется через Base или Derived указатель. Таким образом, я не вижу проблемы с вашим подходом, мне кажется, это нормально.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector