Почему мой метод переопределения не вызывается в C ?

#c #overriding #c 17

#c #переопределение #c 17

Вопрос:

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

 class TextInput
{
public:
    std::string value;
    void add(char c) 
    {
        if (!value.empty()) 
        {
            value  = c;
        }
        else
        {
            value = c;
        }
    }

    std::string getValue() 
    {
        return value; 
    }
};
 

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

 class NumericInput : public TextInput 
{
public:
    TextInput input;
    void add(char c)
    {
        if (isdigit(c)) 
        {
            input.add(c);
        }
    }

    std::string getValue()
    {
        return value;
    }
};
 

Что я хочу знать, так это то, что при таком вызове в моих тестах:

 #ifndef RunTests
int main()
{
    TextInput* input = new NumericInput();
    input->add('1');
    input->add('a');
    input->add('0');
    std::cout << input->getValue();
}
#endif
 

Почему вызов add по-прежнему использует только базовый класс add ? Я бы предположил, что это использует дочерний класс NumericInout, поскольку он создается как один, но при отладке он по-прежнему использует только родительский класс, и я не понимаю, почему?

Комментарии:

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

2. @Sam дочерний add() элемент переопределяет базовый add() , не так ли?

3. Нет, это не так. C так не работает. C — это не Java. Что-то еще должно быть сделано для того, чтобы он действительно переопределил его. Дополнительную информацию см. В вашем учебнике по C .

4. override также может быть полезно.

5.Вам нужно создать add virtual в базовом классе. Использование override модно (повышает надежность), но не обязательно. C — это не Java, которую вы знаете: классы по умолчанию не являются полиморфными. Также наличие TextInput input; в качестве члена дочернего класса, хотя иногда это полезный шаблон, скорее всего, является ошибкой.

Ответ №1:

 class NumericInput : public TextInput 
{
public:
    TextInput input;
 

Это почти наверняка не то, что вы хотите. Если вы наследуете, я не могу придумать веской причины, по которой вы когда-либо хотели бы использовать композицию. Избавьтесь от этой input переменной-члена. При наследовании вы уже получаете все из родительского класса (в том числе value и в этом случае).

Чтобы конкретно вызвать родительский класс add() , вы можете сделать TextInput::add() , чтобы ваш дочерний add мог выглядеть так:

 void add(char c)
{
    if (isdigit(c)) 
    {
        TextInput::add(c); // Call parent add()
    }
}
 

Кроме того, если вы хотите вызвать add() TextInput указатель, который указывает на a NumericInput , и вы хотите, чтобы он разрешал вызов NumericInput ‘s add() , вы должны объявить базовое add() значение равным virtual :

 virtual void add(char c) { // ...
 

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

В целом, это сделает ваш код похожим:

 class TextInput
{
public:
    std::string value;
    virtual void add(char c)  { value  = c; }
    std::string getValue() { return value; }
};

class NumericInput : public TextInput 
{
public:
    void add(char c)
    {
        if (isdigit(c)) { TextInput::add(c); }
    }

    // You already get this for free from the inheritance
    // std::string getValue()
    // {
    //     return value;
    // }
};
 

Живой пример здесь: https://ideone.com/kQxAPo

Комментарии:

1. Добавьте ключевое слово override в add . С таким же успехом можно научить хорошим привычкам.

2. @Yakk-AdamNevraumont: сегодня я чувствую себя очень педантичным: override это идентификатор с особым значением, а не ключевое слово. Хороший ответ, хотя, 1.

3. @bash это контекстное ключевое слово, более разговорное и понятное. Идентификатор со специальным значением является стандартным, а иногда о стандартах говорить бесполезно. для людей с. 😉

4. @Yakk-AdamNevraumont: Я упоминаю об этом только потому, что вы все равно можете использовать override в качестве имени класса, имени функции или имени переменной. Его введение не нарушило существующий код. (см. decltype ).

5. Большое спасибо! Вроде как изучаю это на лету из фона C #. Я ценю подробный ответ!