1

If I have a pure virtual class InterfaceA that consists solely of a pure virtual destructor, why do I have to define the destructor as inline? I I don't I get an error when I try to link it.

Below is an admittedly contrived example, however it illustrates the point. The point does not compile for me using cmake and g++. However, if I change the InterfaceA destructor definition as follows - inline InterfaceA::~InterfaceA(){}; then it compiles.

Why is this? What does the inline keyword do?

// InterfaceA.h, include guards ommitted for clarity
class InterfaceA
{
    public:
        virtual ~InterfaceA() = 0;
};

InterfaceA::~InterfaceA(){};

// A.h, include guards ommitted for clarity
#include "InterfaceA.h"
class A : public InterfaceA
{
    public:
        A(int val)
            : myVal(val){};
        ~A(){};

        int myVal;
};

// AUser.h, include guards ommitted for clarity
#include "InterfaceA.h"
class AUser
{
    public:
        AUser(InterfaceA& anA)
            : myA(anA){};
        ~AUser(){};

        int getVal() const;

    private:
        InterfaceA& myA;
};

// AUser.cpp
#include "AUser.h"
#include "A.h"

int AUser::getVal() const
{
    A& anA = static_cast<A&>(myA);
    return anA.myVal;
}

// main.cpp
#include "AUser.h"
#include "A.h"
#include <iostream>

int main(){
    A anA(1);
    AUser user(anA);
    std::cout << "value = " << user.getVal() << std::endl;
    return 0;
}
wesanyer
  • 982
  • 1
  • 6
  • 27

2 Answers2

1

You have to use the inline keyword when defining functions in header files. If you do not, and the file is included in more than one translation unit, the function will be defined twice (or more times).

The linker error is probably something like "Symbol ... is multiply defined" right?

If you defined the member function in the body of the class, it would be implicitly inline and it would also work.

See this answer

To answer the question "What does the inline keyword do?":

In the old days it would be used to ask the compiler to inline functions i.e. insert the code whenever the function is used instead of adding a function call. Eventually it turned into a simple suggestion since compiler optimizers became more knowledgeable about which functions were inline candidates. These days it is used almost exclusively to define functions in header files that must have external linkage.

imreal
  • 10,178
  • 2
  • 32
  • 48
  • no, inline doesn't have anything with external linkage... directly, it would remove them from external linkage if they become truly inline. A method can't be both virtual and inline though, what – Swift - Friday Pie Aug 15 '17 at 08:27
  • @Swift I did not say that `inline` declares a method with external linkage. I said that if you want to to have external linkage and define it in a header, it must be `inline`. Also a method can be inlined and have external linkage, you can always get its address from other translation units. Also also, yes a method can be virtual and inline. – imreal Aug 15 '17 at 15:31
0

inline means that compiler is allowed to add code directly to where the function was called. It also removes function from external linkage, so both your compile units would have local version of.. pure destructor.

// InterfaceA.h, include guards ommitted for clarity
class InterfaceA
{
    public:
        virtual ~InterfaceA() = 0;
};

You declare destructor virtual, so compiler almost never would make it inline. Why? because virtual functions are called through vtable - a internal working of virtual functions system, vtable most likely implemented as an array of pointers to member functions. If function is inlined, it would have no address, no legal pointer. If attempt to get address of function is taken, then compiler silently disregards inline keyword. The other effect will be still in place: inlined destructor stops to be visible to linker.

It may look like declaring pure virtual destructor looks like oxymoron , but it isn't. The pure destructor is kind of destructor that would be always called without causing UB. Its presence would make class abstract, but the implicit call in sequence of destructor calls would still happen. If you didn't declare destructor body, it would lead to an UB, e.g. purecall exception on Windows.

If you don't need an abstract base class, then inline definition will suffice:

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

that is treated by compiler as inline as well, but mixing inline definition and pure member declaration is not allowed.

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42