2016-07-29 7 views
8
  • В C++ 11 override specifier защищает от переопределения предполагаемой функции виртуальной базы (поскольку подписи не совпадают).
  • final specifier защищает от непреднамеренного переопределения функции в производном классе.

=> Есть спецификатор (что-то вроде, может быть first или no_override), которая защищает от переопределения неизвестной базовой функции?Какова противоположность спецификатора C++ `override` /` final`?

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


EDIT 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; meaing в PainterPath
  • class C : B реализует virtual void showPath() override { mPath.setVisible(); }
  • Теперь class A получает новый virtual void showPath(); означает путь к файлу
  • Теперь, когда A вызывает showPath(), B показывает PainterPath вместо некоторого пути к файлу.

Конечно, это не так, и я должен переименовать 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 (ссылка может быть истек)

+3

Возможно, опция '-Wsuggest-override' GCC может помочь, см. Https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html – gavv

+0

@gavv Вот что я ищу. Существует ли независимый от платформы способ? Мне тоже это нужно в VS2012. –

+1

ИМХО это вопрос о дизайне. Как упоминала Батшеба, это невозможно, за исключением использования окончательного в базовом классе. Или вы можете использовать трюк, о котором упоминал Леон, который нарушает цель класса интерфейса ([см. Здесь] (http://coliru.stacked-crooked.com/a/17346a88fa6aaa89)) и содержит некоторые другие проблемы. Как вы упомянули в своем последнем правлении, вы просто не должны называть функции одинаковыми с разными значениями. если вы хотите получить информацию об использовании некоторых инструментов анализа (также упоминается в «Bathsheba imho» только правильный ответ). (IMHO) – user1810087

ответ

2

Этот ответ является общедоступной wiki, потому что он объединяет в себе все другие ответы. Пожалуйста, поддержите конкретный ответ, который был вам полезен, а также этот.

  1. Нет, нет спецификатора как first или no_override.(answer)
  2. Вы должны использовать спецификатор override как можно чаще.
    Qt имеет macro Q_DECL_OVERRIDE, который расширяется до override, если таковой имеется.
    Если нет, по крайней мере, отметьте каждую функцию переопределения комментарием.
  3. Если вы сделаете это, есть флаги компилятора, которые предупреждают о пропавшем override:
    «Clang now has -Winconsistent-missing-override, and newer GCCs have -Wsuggest-override.»
    Я не знаю флага VS2012 для этого. Не стесняйтесь редактировать.
  4. Вы можете подражать желаемому поведению, добавив «секрет», который не может знать базовый класс.(answer)
    Это полезно в особых случаях использования, но в целом нарушает концепцию виртуальности (см. Комментарии к другим ответам).
  5. Если вы не являетесь владельцем базового класса и имеете конфликт (например, предупреждение компилятора), вам нужно будет переименовать свою виртуальную функцию во всех производных классах.
  6. Если у вас есть базовый класс, вы можете временно добавить final в любую новую виртуальную функцию. (answer)
    После того, как код компилируется без ошибок, вы знаете, что никакой функции этого имени и подписи не существует в любом производном классе, и вы можете снова удалить final.

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

+0

Отличный обзор. – gavv

2

Нет там нет.

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

(Конечно, вы могли бы отметить вашу новую функцию final просто проверить, что функция класса ребенок не собирается избить его.)

Ваш единственный вариант прибегнуть к инструментам анализа кода.

(Обратите внимание, что VS2012 не реализует, или даже требовать для реализации, стандарт C++ 11, хотя у него есть некоторые из них.)

+2

Как это может не нарушить существующую функциональность? Перегруженная виртуальная функция может выполняться в неожиданных/недействительных ситуациях. –

+2

Когда? Я не могу придумать ситуацию. Представьте встречный пример (естественно, естественно, с нисходящим). Это может вылечить мое похмелье. – Bathsheba

+0

@Martin: Невозможно вызвать произвольную «наименее производную» функцию, насколько мне известно. Вам нужно будет квалифицировать имя (например, 'Base :: foo()'), и тогда вы уже должны знать, что функция существует. –

1

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()) 
    { 
    } 
}; 
+1

У этого есть потенциал, но 'foo' должен быть виртуальным. Может быть, структура 'protected'? 'protected'ness работает« вниз », а не« вверх », если вы получите мой смысл. – Bathsheba

+0

'// Эта функция никогда не будет переопределять функцию из Base', потому что она никогда не может быть виртуальной в базовом классе! – user1810087

+0

@ user1810087 Он может быть виртуальным, но затем DontOverride должен быть защищен, чтобы подклассы могли его переопределить. – Leon

2

Объект спецификаторов как first или no_override не там как таковой. Наверное, потому что это может создать путаницу. Однако это может быть тривиально достигнуто путем изменения подхода.

Необходимо добавить новый метод в базовый класс с помощью спецификатора final. Это поможет получить ошибку компилятора для любых совпадающих подписей. Потому что это сделает последующие сигнатуры метода производного класса автоматически как «первые» в своем роде. Позднее ключевое слово final может быть удалено, так как оно предназначалось только для «проверки первой руки».

Ввод & удаление final ключевого слова после того, как вновь добавленный базовый метода является аналогический аналогично компиляцией бинарного с отладочной опцией (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 спецификатор.

+0

Я думаю, что дело в том, что он не может изменить базовый класс. – user1810087

+1

@ user1810087, OP хочет добавить новый метод в базовый класс и намереваться получить ошибку компилятора, если он случайно совпадает с любым из производных классов. Поэтому, когда мы добавляем новый метод с опцией 'final', последующая подпись производного класса автоматически становится« первой »в своем роде. Если это то же самое, компилятор дает ошибку. – iammilind

+0

нет, он хочет получить ошибку компилятора, когда метод ** был добавлен к базе, а не если ** он добавляет ** метод к базе. – user1810087