问题
- In c++11 the override specifier protects from not overriding an intended virtual base function (because the signatures do not match).
- The final specifier protects from unintentionally overriding a function in a derived class.
=> Is there a specifier (something like maybe first
or no_override
) that protects from overriding an unknown base function?
I'd like to get a compiler error when a virtual function was added to a base class with the same signature as an already existing virtual function in a derived class.
EDIT 4: To keep this question simple and answers relevant, here is again the
original pseudo-code
- abstract
class B : A
hasprivate: virtual void fooHasBeenDone() = 0;
class C : B
implementsprivate: virtual void fooHasBeenDone() override { react(); }
- Now
class A
gets a newprivate: virtual void fooHasBeenDone();
- But the new
A::foo
could be something different than the originalB::foo
.
and a specific example
- abstract
class B : A
hasvirtual void showPath() = 0;
meaing a PainterPath class C : B
implementsvirtual void showPath() override { mPath.setVisible(); }
- Now
class A
gets a newvirtual void showPath();
meaning a file path - Now when A calls showPath(), B shows the painterPath instead of some file path.
Of course this is wrong, and I should then rename B::showPath()
to B::showPainterPath()
and implement B::showPath() override
as well. I'd just like to get informed by the compiler.
Here is a compiling real-world example:
#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;
}
Run it and look at the text output.
For reference, an older example with a specific use-case (PainterPath instance):
https://ideone.com/6q0cPD (link may be expired)
回答1:
The facility of specifiers like first
or no_override
is not there as such. Probably because it may create confusion. However, it can trivially be achieved by changing the approach.
One should add any new method in the base class with final
specifier. This will help to get the compiler error for any matching signatures. Because, it will make the subsequent derived class method signatures automatically as "first" of their kind. Later the final
keyword can be removed, as it was intended just for "first hand verification".
Putting & removing final
keyword after the newly added base method is analogically similar to compiling binary with debug (g++ -g
) option, which helps you to fix bug. In production that debug option is removed for optimization.
From your example:
class A {}; // no method, no worry
class B {
public: virtual void showPath() = 0; // ok
};
...
Now accidentally you are adding similar method in A
, that results in error:
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
};
So the signatures between new A::showPath()
& existing B::showPath()
have to be negotiated & then carry on by removing final
specifier.
回答2:
No there is not.
Adding a virtual function to a base class that has the same signature as a virtual function in a child class cannot break any existing functionality unless adding that virtual function turns the base class into a polymorphic type. So in the norm, it's benign, and a purest would argue, adding language features to guard against this would be rather pointless.
(Of course you could mark your new function final
just to check that a child class function isn't going to clobber it.)
Your only option is to resort to code analysis tools.
(Note that VS2012 does not implement, or even claim to implement, the C++11 standard, although it does have some of it.)
回答3:
This answer is community wiki because it combines all other answers. Please upvote the specific answer that was helpful to you as well as this one.
- No, there is no specifier like
first
orno_override
. (answer) - You should use the override specifier as often as possible.
Qt has a macro Q_DECL_OVERRIDE that expands tooverride
, if available.
If not available, at least mark each overriding function with a comment. - If you do that, there are compiler flags that warn about a missing
override
:
"Clang now has -Winconsistent-missing-override, and newer GCCs have -Wsuggest-override."
I don't know of a VS2012 flag for this. Feel free to edit. - You can mimic the desired behavior by adding a 'secret' that the base class cannot know. (answer)
This is helpful in very specific use cases, but generally breaks the concept of virtuality (see comments to the other answers). - If you don't own the base class and have a conflict (e.g. compiler warning), you will need to rename your virtual function in all derived classes.
- If you own the base class, you can temporarily add a
final
to any new virtual function. (answer)
After the code compiles without errors, you know that no function of that name and signature exists in any derived class, and you can remove thefinal
again.
... I think I'll start marking first virtual functions as DECL_FIRST
. Maybe in the future there will be a compiler-independent way of checking this.
回答4:
C++ doesn't seem to provide such means out of the box. But you can mimic it like follows:
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())
{
}
};
If you intend to introduce a new virtual function, then do it like below:
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())
{
}
};
来源:https://stackoverflow.com/questions/38656443/what-is-the-opposite-of-c-override-final-specifier