Подтвердить что ты не робот

Хорошая практика: аргументы по умолчанию для чистого виртуального метода

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

class Base {
    ...
    virtual someMethod(const SomeStruct& t = 0) = 0;
    ...
}

class Derived : public Base {
    ...
    virtual someMethod(const SomeStruct& t = 0);
    ...
}

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

4b9b3361

Ответ 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*...

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

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

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