2015-01-15 5 views
4

Я прочитал о механизме наследования в C++ и о виртуальных функциях.параметр по умолчанию в виртуальных функциях C++

согласно моему знанию (во всех примерах, с которыми я столкнулся) унаследованные методы имели ту же подпись, что и родительский класс.

Мой вопрос: Я знаю, что значение параметра по умолчанию функции не является частью сигнатуры функции.

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

В этом случае, когда я вызываю метод производного объекта, используя указатель на родительский класс, будет ли функция вызываться с/без этой инициализации по умолчанию?

благодаря

+4

Ну, вы попробовали? Это вопрос минут ... – zegkljan

+1

@ JanZegklitz Я согласен с тем, что вы можете легко попробовать это, если знаете, что попробовать, но это * - это запутанная ситуация, и я думаю, что ответ, объясняющий, что происходит, может быть полезен для многие. В конце концов, «я попробовал это с моей версией моего компилятора, и это произошло» не очень хорошая мера для того, как что-то действительно работает на C++. – 5gon12eder

ответ

6

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

Поэтому параметр по умолчанию выбирается компилятором, используя статический объект , на который вызывается функция-член. Давайте посмотрим пример.

#include <iostream> 
#include <memory> 

class Base 
{ 

public: 

    virtual void 
    f(int a, int b = 1) 
    { 
    std::cout << "Base: a = " << a << ", b = " << b << "\n"; 
    } 
}; 

class Derived : public Base 
{ 

public: 

    virtual void 
    f(int a = 1, int b = 2) override 
    { 
    std::cout << "Derived: a = " << a << ", b = " << b << "\n"; 
    } 
}; 

int 
main() 
{ 
    std::unique_ptr<Base> base_as_base {new Base {}}; 
    std::unique_ptr<Base> derived_as_base {new Derived {}}; 
    std::unique_ptr<Derived> derived_as_derived {new Derived {}}; 
    base_as_base->f(0);  // Base: a = 0, b = 1 
    derived_as_base->f(0);  // Derived: a = 0, b = 1 
    // derived_as_base->f(); // compiler error 
    derived_as_derived->f(0); // Derived: a = 0, b = 2 
    derived_as_derived->f(); // Derived: a = 1, b = 2 
} 

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

#include <iostream> 
#include <memory> 

class Base 
{ 

public: 

    void 
    f(int a, int b = 1) 
    { 
    this->impl(a, b); 
    } 

protected: 

    virtual void 
    impl(int a, int b) 
    { 
    std::cout << "Base: a = " << a << ", b = " << b << "\n"; 
    } 
}; 

class Derived : public Base 
{ 

protected: 

    virtual void 
    impl(int a, int b) override 
    { 
    std::cout << "Derived: a = " << a << ", b = " << b << "\n"; 
    } 
}; 

int 
main() 
{ 
    std::unique_ptr<Base> base_as_base {new Base {}}; 
    std::unique_ptr<Base> derived_as_base {new Derived {}}; 
    std::unique_ptr<Derived> derived_as_derived {new Derived {}}; 
    base_as_base->f(0);  // Base: a = 0, b = 1 
    derived_as_base->f(0);  // Derived: a = 0, b = 1 
    derived_as_derived->f(0); // Derived: a = 0, b = 1 
} 
+0

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

2

Это то, что я нашел из ++ проект стандарта N3337 C:

8.3.6 аргументы по умолчанию

10 Вызов виртуальной функции (10.3) использует аргументы по умолчанию в объявление виртуальной функции, определяемое статическим типом указателя или ссылки, обозначающей объект. Функция переопределения в производном классе не получает аргументы по умолчанию от функции, которую она переопределяет.[Пример:

struct A { 
    virtual void f(int a = 7); 
    }; 
    struct B : public A { 
    void f(int a); 
    }; 
void m() { 
    B* pb = new B; 
    A* pa = pb; 
    pa->f(); // OK, calls pa->B::f(7) 
    pb->f(); // error: wrong number of arguments for B::f() 
} 

- конец пример]

Coming на ваш вопрос:

Могу ли я определить это значение, чтобы быть некоторая константа в виртуальной функции родительского класса ,

Да

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

Да.

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

+0

Плюс 1 для стандартной цитаты. Я искал официальную ссылку, но не смог найти ее быстро. – 5gon12eder

Смежные вопросы