C++11 interface pure virtual destructor
Asked Answered
P

3

9

UPD. There is a mark that it is a duplicate of this question. But in that question OP asks HOW to use default to define pure virtual destructor. This question is about what the difference.

In C++ (latest standard if possible) what the real difference between defining pure virtual destructor with empty body implementation and just a empty body (or default)?

Variant 1:

class I1 {
public:
    virtual ~I1() {}
};

Variant 2.1:

class I21 {
public:
    virtual ~I21() = 0;
};

I21::~I21() {}

Variant 2.2:

class I22 {
public:
    virtual ~I22() = 0;
};

I22::~I22() = default;

Update I found at least 1 difference between Variant 1 and Variants 2.1/2.2:

std::is_abstract::value is false for Variant 1, and true for Variants 2.1 and 2.2.

Demo

May be someone can found difference between 2.1 and 2.2?

Pachston answered 12/6, 2016 at 18:19 Comment(1)
What is the question regarding virtual inheritance?Carliecarlile
C
15

The difference between I1 and I2*, as you pointed out, is that adding = 0 makes the class abstract. In fact, making the destructor pure virtual is a trick to make a class abstract when you don't have any other function to be pure virtual. And I said it's a trick because the destructor cannot be left undefined if you ever want to destruct any derived class of it (and here you will), then you still need to define the destructor, either empty or defaulted.

Now the difference between empty or defaulted destructor/constructor (I21 and I22) is way more obscure, there isn't much written out there. The recommended one is to use default, both as a new idiom to make your intentions clearer, and apparently, to give the compiler a chance for optimization. Quoting msdn

Because of the performance benefits of trivial special member functions, we recommend that you prefer automatically generated special member functions over empty function bodies when you want the default behavior.

There are no visible differences between the two, apart from this possible performance improvement. = default is the way to go from C++11 on.

Counterpoint answered 16/6, 2016 at 3:22 Comment(0)
M
4

All I could find was:

§12.4 (5.9)

A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

leading to:

§10.4 (the class is now abstract)

10.4 (2) says:

A pure virtual function need be defined only if called with, or as if with (12.4), the qualified-id syntax (5.1).

But the narrative on destructors in §12.4 talks about destructors always being called as if by their fully qualified name (in order to prevent ambiguity).

Which means that:

  • the destructor must be defined, even if pure virtual, and

  • the class is now abstract.

Marissamarist answered 12/6, 2016 at 20:6 Comment(2)
Minor note: there's nothing in these paragraphs that a destructor must be defined, quite the contrary. If you wanted a 'static class' (a class with every member / member function being static), you don't need a destructor.Scythe
@Scythe provided you don't create an object of that class, yes. you're right. One wonders though, why you would declare a destructor in that case...Marissamarist
S
2

Variant 1 will allow you to have an instance of the class. Variant 2.1, 2.2 won't allow instances, but allows instances of descendants. This, for example works (and is able to confuse many people), while removing the marked line will make compile fail:

class I21 {
public:
    virtual ~I21() = 0;
};

I21::~I21() {} // remove this and it'll not compile

class I22 : public I21
{
public:
    virtual ~I22() {}
};

int main() {
    I22 i;
    return 0;
}

The reason behind, the destructor chain calls I21::~I21() directly and not via interface. That said, it's not clear what your goal is with pure virtual destructors. If you'd like to avoid instantiation (i.e., static class), you might consider deleting the constructor instead; if you'd like descendants that can be instantiated but not this class, perhaps you need a pure virtual member function that's implemented in descendants.

Scythe answered 12/6, 2016 at 20:21 Comment(1)
Of course in all variants (including Variant 1) I meaned that class will contain other pure virtual members...Pachston

© 2022 - 2024 — McMap. All rights reserved.