переопределить виртуальный метод c ++

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

Можете ли вы дать мне простой пример правильного кода, в котором виртуальный метод родительского класса должен возвращать значение (но его реализация предоставляется в дочернем классе), а значение, возвращаемое виртуальным методом в родительском классе, используется в другом методы этого класса. Потому что я где-то видел (например, здесь: Безопасное переопределение виртуальных функций C ++), что это может вызовет некоторые проблемы, и определенный пользователем метод заметит переопределение виртуального метода родительского класса.

Примечание: я программирую с помощью Code :: Blocks, используя компилятор g ++.

РЕДАКТИРОВАТЬ: по просьбе здесь простой пример того, что я хочу:

template<typename T>
class parent {
public:
  // Public methods that user can call
  int getSomething(T t);
  void putSomething(T t, int x);

  // public method that user should implement in his code
  virtual float compute(T t) { }

  // protected or private methods and attributes used internally by putSomething ...
  float doComplexeThings(...); // this can call
};

Метод compute () должен быть реализован пользователем (дочерним классом). Однако этот метод compute () вызывается, например, putSomething () и doComplexeThings ().


person shn    schedule 26.01.2012    source источник
comment
Для того, что вы описываете, виртуальный метод может не понадобиться (статический полиморфизм может работать для вас) - трудно сказать по вашему описанию. Отправьте пример кода, который вы написали, чтобы мы лучше понимали, что вы пытаетесь сделать.   -  person kfmfe04    schedule 26.01.2012
comment
Пример в связанном вопросе касался того, что вы считаете переопределением, но на самом деле объявляет перегрузку из-за ошибки, например константа отсутствует и не касается возвращаемых значений. Как вы думаете, в чем конкретно заключается опасность?   -  person CashCow    schedule 26.01.2012
comment
В классическом коде шаблона вам даже не нужно объявлять virtual float compute(T t). Вы просто используете его (вызываете) внутри putSomething () и doComplexeThings () через t.compute(), если у вас есть дескриптор экземпляра. Компилятор выдаст ошибку, если ваш класс T не реализует вычисление. Таким образом, родительский элемент и T на самом деле даже не должны жить в одной и той же иерархии наследования: то есть, T является дочерним элементом родительских отношений, не требуется. Это также может дать вам возможность дать родителю более значимое имя (поскольку связь is-a не обязательна).   -  person kfmfe04    schedule 26.01.2012
comment
@ kfmfe04 Я не понимаю, почему вы говорите об отношениях между T и родителем. T не обязательно является классом, это просто тип, который использует шаблон класса (родительский), потому что я хочу сделать его независимым от типа обрабатываемых данных. Забудьте о T, моя проблема только в том, где и как определить метод float compute(...), который вызывается doComplexeThings(...) и putSomething(...), в то время как код метода float compute(...) должен быть предоставлен пользователем.   -  person shn    schedule 26.01.2012


Ответы (3)


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

class parent {
public:
  // pure virtual method must be provided in subclass
  virtual void handle_event(int something) = 0; 
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

class incomplete_child : public parent {
public:
  virtual void handle_event(int something) const {
    // does not override the pure virtual method
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1); // will call child::handle_event
  parent *p = new incomplete_child(); // will not compile because handle_event
                                      // was not correctly overridden
}
person mschneider    schedule 26.01.2012
comment
Ковариантные возвращаемые типы разрешены для виртуальных методов. - person Alok Save; 26.01.2012
comment
@als, лишь небольшая деталь для «ковариантные возвращаемые типы разрешены», ковариантные типы разрешены только в том случае, если функция возвращает ссылку или указатель. Я знаю, что вы это знаете, но оставляю этот комментарий для других. - person David Rodríguez - dribeas; 26.01.2012
comment
@mschneider Проблема с вашим ответом заключается в том, что вы меняете семантику только для того, чтобы избежать потенциальной проблемы. В исходном случае функция присутствовала, но могла быть перегружена, в предлагаемом вами решении она должна быть перегружена. Это не устраняет ошибки при перегрузке существующей функции, а скорее заставляет производные классы перегрузиться. - person David Rodríguez - dribeas; 26.01.2012

Если вы можете использовать функции C ++ 11 в своем компиляторе, то переопределения можно пометить соответствующим образом специальным идентификатором override:

 float compute() override;

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

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

person David Rodríguez - dribeas    schedule 26.01.2012
comment
+1 за интересную функцию C ++ 11 - помогает убедиться, что ваш метод действительно что-то переопределяет - person kfmfe04; 26.01.2012

Этот вопрос задан в 2013 году. Он довольно старый, но я обнаружил что-то новое, чего нет в ответах.

Нам необходимо понять три концепции: перегрузка, перезапись и скрытие.

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

class A {
public:
  virtual void print() {
    cout << id_ << std::endl;
  }
private:
  string id_ = "A";
};

class B : A {
public:
  using A::print;
  void print(string id) {
    std::cout << id << std::endl;
  }
};

int main(int argc, char const *argv[]) {
  /* code */
  A a;
  a.print();
  B b;
  b.print();
  b.print("B");
  return 0;
}

Добавьте using A :: print; в производный класс, и все будет работать!

Хотя я не считаю это хорошей идеей, поскольку философия, лежащая в основе перегрузки и наследования, различается, объединять их вместе - не лучший вариант.

person yuan    schedule 21.02.2018