Хорошая практика: аргументы по умолчанию для чистого виртуального метода
Я создал абстрактный базовый класс, который имеет чистый виртуальный метод с аргументом по умолчанию.
class Base {
...
virtual someMethod(const SomeStruct& t = 0) = 0;
...
}
class Derived : public Base {
...
virtual someMethod(const SomeStruct& t = 0);
...
}
Итак, я хотел бы знать, является ли хорошей практикой установить аргумент по умолчанию для чистого виртуального и общего для виртуальных методов?
Ответы
Ответ 1
Я часто хочу использовать как параметры по умолчанию, так и виртуальную функцию, как и вы. Другие справедливо отметили, что это приводит к двусмысленности и, как правило, не очень хорошая идея. Существует довольно простое решение, которое я использую. Дайте вашей виртуальной функции другое имя, сделайте ее защищенной, а затем предоставите публичную функцию с параметрами по умолчанию, которые ее называет.
class Base {
protected:
virtual void vSomeMethod(const SomeStruct& t ) = 0;
public:
void someMethod( const SomeStruc& t = 0 )
{ vSomeMethod( t ); }
}
Производные классы просто переопределяют vSomeMethod
и не беспокоятся о параметрах по умолчанию.
Ответ 2
На самом деле, ваш код является одним из худших возможных шаблонов использования для параметров по умолчанию, поскольку он включает как наследование, так и полиморфное поведение. Я поддерживаю совет, чтобы посмотреть на связанный отзыв Скотта Мейерса, но вот краткий обзор:
В случае полиморфных вызовов параметры по умолчанию используются в соответствии с объявлением для статического типа, а не динамическим. Логично, поскольку время выполнения не имеет представления о параметрах по умолчанию, но нарушает любые разумные предположения о полиморфном поведении. Например,
#include <cstdio>
class Base
{
public:
virtual void f(int a = 1)
{
printf("Base::f(%d)\n", a);
}
};
class Deriv : public Base
{
public:
virtual void f(int a = 2)
{
printf("Deriv::f(%d)\n", a);
}
};
int main()
{
Base* a = new Deriv();
a->f();
delete a;
return 0;
}
дает:
Deriv::f(1)
Ответ 3
Не используйте параметры по умолчанию, если это возможно, но если вы это сделаете, никогда не переопределяйте их (см. текст для подробностей)
Купите как эффективные книги на C++, так и Скотта Мейерса. Вы не пожалеете об этом.
Ответ 4
Я бы:
- определить виртуальные функции с параметром (без значения по умолчанию)
- определяют не виртуальные функции в базовом классе, без параметра at
все, которые вызывают виртуальную функцию, передающую правильный параметр по умолчанию
Таким образом, производные классы вообще не должны заботиться о значении по умолчанию.
Ответ 5
Если вы хотите, чтобы этот код имел смысл:
Base* p = new Derived;
p->someMethod();
так как статический тип p
равен Base*
, это базовая подпись, которая используется при вызове.
Значение по умолчанию назначается и, будучи функцией virtual, вызов перенаправляется на Derived.
Вы даже можете определить их по-разному, если хотите, чтобы ваш Derived:: someMethod получил другое значение от Base*
, а не Derived*
...
Важно то, что эти "отношения" хорошо документируют, так как большинство программистов не понимают их из простого чтения кода.
Конечно, если все, что вам не подходит в конкретном контексте, создавая больше путаницы, чем другие, избегайте параметров по умолчанию для виртуальных функций и используйте вспомогательный не виртуальный, чтобы правильно их называть.
Но учитывая также, что, по мнению читателя, параметр по умолчанию более объяснительный, чем функция перегрузки, вызывающая частным образом другую с недопустимым изменением параметров.