Virtual function

Pages: 1234
> The only way you can call it is by qualifying the function call with A:: in front like keskiverto did on line 11.

If the destructor is pure virtual with a definition, the destruction of the base class sub-object would invoke it. Also a call to any pure virtual with a definition from within the constructor or destructor of the abstract base class.


> Why would this be useful?

It is actually required if the standard wants to allow the destructor of a class to be pure virtual. The standard makes a specific mention of this:

A prospective destructor can be declared virtual and with a pure-specifier. If the destructor of a class is virtual and any objects of that class or any derived class are created in the program, the destructor shall be defined. http://eel.is/c++draft/class.dtor#12


Repeat:
http://coliru.stacked-crooked.com/a/e8933126cda19320
JLBorges wrote:
It is actually required if the standard wants to allow the destructor of a class to be pure virtual.

Yes, but it still doesn't explain why you would want to make the destructor pure virtual in the first place.
> it still doesn't explain why you would want to make the destructor pure virtual in the first place.

Herb Sutter:
2. Why might you declare a pure virtual function and also write a definition (body)? Give as many reasons or situations as you can.

There are three main reasons you might do this. #1 is commonplace, #2 is pretty rare, and #3 is a workaround used occasionally ...

Most programmers should only ever use #1.

#1. Pure Virtual Destructor
... If the class should be abstract (you want to prevent instantiating it) but it doesn't happen to have any other pure virtual functions, a common technique to make the destructor pure virtual

#2. Force Conscious Acceptance of Default Behaviour
...

#3. Workaround Poor Compiler Diagnostics
...
http://www.gotw.ca/gotw/031.htm


While it (pure virtual destructor) may not be as 'commonplace' as suggested by Sutter, occasionally it can be a useful feature.

A trivialised example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <typeinfo>

// a generic abstract interface; the specific interface operations depends on the specific interface
struct interface { virtual ~interface() = 0 ; } ; inline interface::~interface() = default ;

// a specific abstract interface; it declares a specific interface operation
struct some_interface : interface { virtual void do_something() const = 0 ; } ;

template < typename T > void try_do_something( T& object ) // do_something if the object supports some_interface
{
    if( auto itf = object.get_interface( typeid(some_interface) ) )
        if( auto some_itf = dynamic_cast< const some_interface* >(itf) ) return some_itf->do_something() ;

    std::cout << "this object does not currently support some_interface\n" ;
}

int main()
{
    struct A : private some_interface // expose some_interface if a a run-time condition is satisfied
    {
        // conditionally support some_interface
        const interface* get_interface( const std::type_info& tinfo ) const
        { return tinfo == typeid(some_interface) && can_do_something ? this : nullptr ; }

        virtual void do_something() const override { std::cout << "A::some_interface::do_something()\n" << '\n' ;}

        bool can_do_something = true ;
    };

    struct B // implement some_interface via delegation to an  external object
    {
        explicit B( const A& a ) : a(a) {}

        const interface* get_interface( const std::type_info& tinfo ) const { return a.get_interface(tinfo) ; }

        const A& a ;
    };

    A a ;
    B b(a) ;

    try_do_something(b) ;

    a.can_do_something = false ; // don't want to support some_interface now
    try_do_something(b) ;
}

http://coliru.stacked-crooked.com/a/9f483ac236a56da1

In any case, I tend to strongly favour leaving such decisions to the programmer over introducing more special case restrictions in the standard:

"A pure virtual function shall not have a definition; except in the case of a pure virtual destructor."

or "A virtual function may be declared as pure; except for a virtual destructor."
Thank you JLBorges, now I get it.

And now I also understand what keskiverto meant ...
With the quirk one can provide an abstract class, yet include default implementations for all of its member functions.
Last edited on
Thanks you everyone for your good explanation which are really interesting. The examples that you showed are explicit - clever. I really appreciate them ++
Topic archived. No new replies allowed.
Pages: 1234