Наступил давеча на такие грабли. В базовом классе объявлена вирт. ф-ция, которая понятно переопределяется в наследуемых классах. Схема наследования такая:
class Base { virtual foo() = 0; }
class Derived : public Base { virtual foo() {}; }
class Derived1, Derived2, ..., DerivedN : public Derived ;
Основная масса классов пользуется ф-цией, определенной в Derived, но в некоторых из DerivedX требуется переопределять ф-цию foo() специфичным образом. В один прекрасный момент понадобилось изменить прототип ф-ции foo() в классе Base, что и было успешно сделано. Прототип в Derived тоже был изменен ( забыть об этом было нельзя, т.к. foo() в Base чисто виртуальная), а вот про некоторые из классов DerivedX благополучно было забыто (т.к.их достаточно много). Вопрос в том, как писать безопасный код, гарантированно исключающий такие ситуации?
Re: Изменение прототипа вирт ф-ции в базовом классе
В принципе, в таком случае может помочь компилятор — ворнингами. Но не все компиляторы так могут. Вот g++ предупреждает, что виртуальная функция была скрыта, а не переопределена (не знаю, всегда ли он просекает такое)
Of course, the code must be complete enough to compile and link.
Re: Изменение прототипа вирт ф-ции в базовом классе
Здравствуйте, Аноним, Вы писали:
А>Наступил давеча на такие грабли. В базовом классе объявлена вирт. ф-ция, которая понятно переопределяется в наследуемых классах. Схема наследования такая:
Да, ситуация коварная. При изменении сигнатуры функции компилятор может выдать варнинг, но при изменении названия функции никакого варнинга не будет.
Решение 1 (в лоб):
struct A
{
virtual void func()
{
// Implementation
}
};
struct B : A
{
virtual void func()
{
if (false) A::func();
// Implementation
}
};
Плохо то, что при изменении типа параметра функции на приводимый к исходному работать не будет (например, int изменяем на char, хотя может быть варнинг).
Re[2]: Изменение прототипа вирт ф-ции в базовом классе
От:
Аноним
Дата:
08.12.05 14:21
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>В принципе, в таком случае может помочь компилятор — ворнингами. Но не все компиляторы так могут. Вот g++ предупреждает, что виртуальная функция была скрыта, а не переопределена (не знаю, всегда ли он просекает такое)
Уродский VC даже варнинга не выдал
Re: Изменение прототипа вирт ф-ции в базовом классе
Здравствуйте, Аноним, Вы писали:
А>Вопрос в том, как писать безопасный код, гарантированно исключающий такие ситуации?
Увы и ах — на текущий момент "нормального" решения нет. Именно из-за этой проблемы предлагается ввести с С++ новое ключевое слово "override". Не уверен, но по-моему где-то на форуме упоминалось о том, что в VC2005 это ключевое слово есть...
Еще некоторые компиляторы (Comeau например) выдают по такому поводу предупреждение:
#include <iostream>
using namespace std;
struct base
{
virtual void f(int n) = 0;
virtual ~base() {}
};
class d1 : public base
{
virtual void f(int n) { cout << "d1\n"; }
};
class d2 : public d1
{
virtual void f() { cout << "d2\n"; }
};
int main()
{
return 0;
}
"ComeauTest.c", line 18: warning: function "base::f(int)" is hidden by "d2::f" --
virtual function override intended?
virtual void f() { cout << "d2\n"; }
Любите книгу — источник знаний (с) М.Горький
Re: Изменение прототипа вирт ф-ции в базовом классе
Здравствуйте, Аноним, Вы писали:
А>Наступил давеча на такие грабли. В базовом классе объявлена вирт. ф-ция, которая понятно переопределяется в наследуемых классах. Схема наследования такая:
Вариант 2 (как плюс ещё получаем большую гибкость и лучший дизайн):
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Аноним, Вы писали:
А>>Вопрос в том, как писать безопасный код, гарантированно исключающий такие ситуации? B>Увы и ах — на текущий момент "нормального" решения нет. Именно из-за этой проблемы предлагается ввести с С++ новое ключевое слово "override". Не уверен, но по-моему где-то на форуме упоминалось о том, что в VC2005 это ключевое слово есть... B>Еще некоторые компиляторы (Comeau например) выдают по такому поводу предупреждение:
Предупреждение будет только если изменились параметры, а если изменилось название, то не будет никаких варнингов.
Как нет решения? Я два привёл они, несомненно, менее элегантные, что поддержка со стороны языка, но в жизни применять можно.
> Основная масса классов пользуется ф-цией, определенной в Derived, но в некоторых из DerivedX требуется переопределять ф-цию foo() специфичным образом. В один прекрасный момент понадобилось изменить прототип ф-ции foo() в классе Base, что и было успешно сделано. Прототип в Derived тоже был изменен ( забыть об этом было нельзя, т.к. foo() в Base чисто виртуальная), а вот про некоторые из классов DerivedX благополучно было забыто (т.к.их достаточно много). Вопрос в том, как писать безопасный код, гарантированно исключающий такие ситуации?
Интересная идея. Как развтие темы, можно такой злой макрос написать:
template<class Result, class Base, class Member, Member M>
class override_result
{
public:
typedef Result type;
};
#define OVERRIDE(RESULT, BASE, NAME, ARGS) \
override_result<RESULT, BASE, RESULT (BASE::*) ARGS, &BASE::NAME>::type NAME ARGS
И пример его использование:
class Base
{
public:
virtual int Function(int, const char &) const { return 0; }
};
class Derived: public Base
{
public:
OVERRIDE(int, Base, Function, (int P, const char &A) const)
{
return P + A;
}
};
class A
{
public:
virtual void f() = 0;
virtual ~A() {}
};
class B: A
{
virtual void f() const = 0;
};
warning C4263: 'void B::f(void) const' : member function does not override any base class virtual member function
warning C4264: 'void A::f(void)' : no override available for virtual member function from base 'A'; function is hidden
Господа, не выключайте варнинги своего компилятора!
Правильно работающая программа — просто частный случай Undefined Behavior
Re[5]: Изменение прототипа вирт ф-ции в базовом классе
К сожалению, далеко не все являются счастливыми обладателями компилятора _MSC_VER >= 1400
_W>Во-вторых,
_W>warning C4263: 'void B::f(void) const' : member function does not override any base class virtual member function _W>warning C4264: 'void A::f(void)' : no override available for virtual member function from base 'A'; function is hidden
_W>Господа, не выключайте варнинги своего компилятора!
Мне кажется, тут нужно немного перефразировать: "Господа, включайте все варнинги своего компилятора, которые отключены по умолчанию!" Ибо в VC7.1 C4263 и C4264 off by default
Любите книгу — источник знаний (с) М.Горький
Re[2]: Изменение прототипа вирт ф-ции в базовом классе
Здравствуйте, Bell, Вы писали:
B>Увы и ах — на текущий момент "нормального" решения нет. Именно из-за этой проблемы предлагается ввести с С++ новое ключевое слово "override". Не уверен, но по-моему где-то на форуме упоминалось о том, что в VC2005 это ключевое слово есть...
Эт из С++/CLI (19.4.1 Draft 1.15) Но туда зачем-то всунули name overriding Еще в Desing & Evolution C++ Bjarne Stroustrup писал какая это глупая идея и почему... (ссылки нет).
ref struct B
{
virtual void F() {}
virtual void F(int i) {}
};
ref struct D1: B
{
virtual void F() override {} // explicitly overrides B::F()
};
ref struct D2: B
{
virtual void F() override {} // explicitly overrides B::F()virtual void G(int i) = B::F {} // named override of B::F(int)
};
ref struct D3: B
{
virtual void F() new = B::F {} // named override of B::F()
};
Однако согласно С++/CLI Draft 1.15 20.1
A virtual member function declaration in a native class can contain:
the function-modifier sealed (§19.4.2).
the function-modifier abstract (§19.4.3).
Но нет override... Почему не ясно... Хотя Visual C++ 8.0 компилирует это:
struct A
{
virtual void f();
};
struct B : A
{
virtual void f() override;
};
И в MSDN явно написано, что для native классов можно применять override specifiers (new, abstract, override, sealed).
Самое плохое что стандарт ECMA C++/CLI будет принят в этом месяце
Опять несоответствие стандартов и реализаций...
Draft ECMA C++/CLI брать тут. Но сегодня у них какие-то проблемы с сайтом.
Re: Изменение прототипа вирт ф-ции в базовом классе
class Base
{
virtual void foo() = 0;
};
class Derived : public Base
{
virtual void foo() {}
};
class DerivedN : public Derived
{
virtual void Base::foo() {}
};