And it's also a way to provide a default implementation that can be explicitly called by derived versions.
Code:
class Base
{
public:
virtual void foo() = 0;
};
void Base::foo() {
// Do some default action.
}
class Derived1 : public Base
{
public:
virtual void foo() {
// Do some preliminary work.
Base::foo(); // Do the default thing.
}
};
class Derived2 : public Base
{
public:
virtual void foo() {
// Do your own thing.
}
};
Note that if you want to force the call to the base, the non-virtual interface pattern is a better solution.
Code:
class Base
{
virtual void foo_prologue() = 0;
public:
void foo() {
foo_prelim();
// Do default thing.
}
};
class Derived : public Base
{
virtual void foo_prologue() {
// Do preliminary work.
}
};
Yes, you can override private functions if they're virtual.
The NVI pattern is also what you should use instead of default parameters in virtual situations. Or rather, to get the default parameter away from the virtual function, where it's seriously harmful.
Code:
class Base
{
virtual void do_foo(const std::string &s);
public:
void foo(const std::string &s = std::string()) { do_foo(s); }
};
Then override do_foo in derived classes.
A pure virtual constructor or destructor makes no sense since calling a pure virtual function will crash your application.
Constructors can't be virtual at all. They're not part of this.
It's not quite that simple. Doing a polymorphic call to a pure virtual function will crash your application. Doing a non-polymorphic call to the implementation will not. However, if there is no implementation, the non-polymorphic call will result in a linker error. That's why you need to implement even a pure virtual destructor.
The real effect of a pure virtual function is not that there is no implementation. It's that the class is abstract, and all derived classes will be, too, unless they implement the function. Having a pure virtual destructor is useful when you need the class to be abstract but there's no other function you want to be pure virtual. With any other function, the derived class would have to provide an implementation, even if it did nothing but call the base version. With the destructor, such an implementation is automatically generated.