override
спецификатор защищает от переопределения предполагаемой виртуальной базовой функции (поскольку сигнатуры не совпадают).final
спецификатор защищает от непреднамеренного переопределения функции в производном классе.=> Есть ли спецификатор (что-то вроде может быть first
или же no_override
) что защищает от переопределения неизвестной базовой функции?
Я хотел бы получить ошибку компилятора, когда виртуальная функция была добавлена в базовый класс с той же сигнатурой, что и существующая виртуальная функция в производном классе.
РЕДАКТИРОВАТЬ 4: Чтобы этот вопрос был простым, а ответы — актуальными,
оригинальный псевдокод
class B : A
имеет private: virtual void fooHasBeenDone() = 0;
class C : B
инвентарь private: virtual void fooHasBeenDone() override { react(); }
class A
получает новый private: virtual void fooHasBeenDone();
A::foo
может быть что-то другое, чем оригинал B::foo
,и конкретный пример
class B : A
имеет virtual void showPath() = 0;
По пути PainterPathclass C : B
инвентарь virtual void showPath() override { mPath.setVisible(); }
class A
получает новый virtual void showPath();
имеется в виду путь к файлуКонечно, это неправильно, и я должен переименовать B::showPath()
в B::showPainterPath()
и реализовать B::showPath() override
также. Я просто хотел бы получить информацию от компилятора.
Вот компиляция пример из реальной жизни:
#include <iostream>
#define A_WITH_SHOWPATH
class A
{
#ifdef A_WITH_SHOWPATH
public:
void setPath(std::string const &filepath) {
std::cout << "File path set to '" << filepath << "'. Display it:\n";
showPath();
}
// to be called from outside, supposed to display file path
virtual void showPath() {
std::cout << "Displaying not implemented.\n";
}
#else
// has no showPath() function
#endif
};
class B : public A
{
public:
virtual void showPath() = 0; // to be called from outside
};
class C1 : public B {
public:
virtual void showPath() override {
std::cout << "C1 showing painter path as graphic\n";
}
};
class C2 : public B {
public:
virtual void showPath() override {
std::cout << "C2 showing painter path as widget\n";
}
};int main() {
B* b1 = new C1();
B* b2 = new C2();
std::cout << "Should say 'C1 showing painter path as graphic':\n";
b1->showPath();
std::cout << "---------------------------\n";
std::cout << "Should say 'C2 showing painter path as widget':\n";
b2->showPath();
std::cout << "---------------------------\n";
#ifdef A_WITH_SHOWPATH
std::cout << "Should give compiler warning\n or say \"File path set to 'Test'. Display it:\"\n and \"Displaying not implemented.\",\n but not \"C1 showing painter path as graphic\":\n";
b1->setPath("Test");
std::cout << "# Calling setPath(\"Test\") on a B pointer now also displays the\n# PainterPath, which is not the intended behavior.\n";
std::cout << "# The setPath() function in B should be marked to never override\n# any function from the base class.\n";
std::cout << "---------------------------\n";
#endif
return 0;
}
Запустите его и посмотрите на вывод текста.
Для справки, более старый пример с конкретным вариантом использования (экземпляр PainterPath):
https://ideone.com/6q0cPD (ссылка может быть просрочена)
Объект спецификаторов, как first
или же no_override
не существует как таковой. Возможно, потому что это может создать путаницу. Тем не менее, это может быть легко достигнуто путем изменения подхода.
Нужно добавить любой новый метод в базовый класс с помощью final
спецификатор. Это поможет получить ошибку компилятора для любых совпадающих сигнатур. Потому что это сделает последующие производные сигнатуры метода класса автоматически «первыми» в своем роде. Позже final
ключевое слово может быть удалено, как это было предназначено только для «проверки из первых рук».
Ввод & удаление final
Ключевое слово после вновь добавленного базового метода аналогично тому, как компилировать двоичный файл с помощью debug (g++ -g
) вариант, который поможет вам исправить ошибку. В производстве эта опция отладки удалена для оптимизации.
Из вашего примера:
class A {}; // no method, no worry
class B {
public: virtual void showPath() = 0; // ok
};
...
Теперь вы случайно добавляете подобный метод в A
, что приводит к ошибке:
class A {
public: virtual void showPath() final; // same signature by chance
// remove the `final` specifier once the signature is negotiated
};
class B {
public: virtual void showPath() = 0; // ERROR
};
Так что подписи между новыми A::showPath()
& существующий B::showPath()
должны быть предметом переговоров & затем продолжить, удалив final
спецификатор.
Нет там нет.
Добавление виртуальной функции в базовый класс с такой же сигнатурой, что и у виртуальной функции в дочернем классе не могу перерыв любой существующий функционал если только добавление этой виртуальной функции не превращает базовый класс в полиморфный тип. Таким образом, в норме это мягко, и самый чистый мог бы поспорить, добавление языковых возможностей для защиты от этого было бы довольно бессмысленным.
(Конечно, вы можете отметить вашу новую функцию final
просто для того, чтобы проверить, что функция дочернего класса не собирается засорять ее.)
Ваш единственный вариант — прибегнуть к инструментам анализа кода.
(Обратите внимание, что VS2012 не реализует и даже не претендует на реализацию стандарта C ++ 11, хотя и имеет некоторые из них.)
Этот ответ является вики сообщества, потому что он объединяет все остальные ответы. Пожалуйста, подтвердите конкретный ответ, который был полезен для вас, а также этот.
first
или же no_override
, (ответ)override
Спецификатор как можно чаще.Q_DECL_OVERRIDE
что расширяется до override
, если доступно.override
:-Winconsistent-missing-override
и более новые GCC имеют -Wsuggest-override
,«final
к любой новой виртуальной функции. (ответ)final
снова.… Думаю начну маркировку первый виртуальные функции как DECL_FIRST
, Возможно, в будущем появится независимый от компилятора способ проверки этого.
C ++, кажется, не предоставляет такие средства из коробки. Но вы можете имитировать это следующим образом:
template<class Base>
class Derived : public Base
{
private:
struct DontOverride {};
public:
// This function will never override a function from Base
void foo(DontOverride dummy = DontOverride())
{
}
};
Если вы намерены ввести новый виртуальная функция, затем сделайте это, как показано ниже:
template<class Base>
class Derived : public Base
{
protected:
struct NewVirtualFunction {};
public:
// This function will never override a function from Base
// but can be overriden by subclasses of Derived
virtual void foo(NewVirtualFunction dummy = NewVirtualFunction())
{
}
};