14

I have a link error where the linker complains that my concrete class's destructor is calling its abstract superclass destructor, the code of which is missing.

This is using GCC 4.2 on Mac OS X from XCode.

I saw g++ undefined reference to typeinfo but it's not quite the same thing.

Here is the linker error message:

Undefined symbols:
  "ConnectionPool::~ConnectionPool()", referenced from:
      AlwaysConnectedConnectionZPool::~AlwaysConnectedConnectionZPool()in RKConnector.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

Here is the abstract base class declaration:

class ConnectionPool {
public:
    static ConnectionPool* newPool(std::string h, short p, std::string u, std::string pw, std::string b);   
    virtual ~ConnectionPool() =0;
    virtual int keepAlive() =0;
    virtual int disconnect() =0;
    virtual sql::Connection * getConnection(char *compression_scheme = NULL) =0;
    virtual void releaseConnection(sql::Connection * theConnection) =0;
};

Here is the concrete class declaration:

class AlwaysConnectedConnectionZPool: public ConnectionPool {
protected:
    <snip data members>
public:
    AlwaysConnectedConnectionZPool(std::string h, short p, std::string u, std::string pw, std::string b);   
    virtual ~AlwaysConnectedConnectionZPool();
    virtual int keepAlive();    // will make sure the connection doesn't time out. Call regularly
    virtual int disconnect();   // disconnects/destroys all connections.
    virtual sql::Connection * getConnection(char *compression_scheme = NULL);
    virtual void releaseConnection(sql::Connection * theConnection);
};

Needless to say, all those members are implemented. Here is the destructor:

AlwaysConnectedConnectionZPool::~AlwaysConnectedConnectionZPool()
{
    printf("AlwaysConnectedConnectionZPool destructor call");  // nothing to destruct in fact
}

and also maybe the factory routine:

ConnectionPool* ConnectionPool::newPool(std::string h, short p, std::string u, std::string pw, std::string b)
{
    return new AlwaysConnectedConnectionZPool(h, p, u, pw, b);
}

I can fix this by artificially making my abstract base class concrete. But I'd rather do something better. Any idea?

Thanks

Community
  • 1
  • 1
Jean-Denis Muys
  • 6,772
  • 7
  • 45
  • 71

2 Answers2

41

Even if you declare a destructor as a pure virtual function, you must provide an implementation for it. Although you cannot instantiate an abstract class directly, it is always instantiated when you instantiate one of its derived (concrete) classes. And so at some point such instances will be destroyed, thus requiring a destructor. The implementation of the pure virtual destructor can be (and normally is) an empty function:

ConnectionPool::~ConnectionPool() {
}
Andres Bucci
  • 947
  • 1
  • 9
  • 37
  • 2
    You answer imply that, but it's worth emphasizing that in C++ abstract methods *can* have implementations. I was honestly surprised when I found out, before that I always thought of methods as either abstract or having implementation, not both. – sbk Mar 31 '10 at 17:32
  • @sbk That's incorrect. In C++ abstract means _pure_ virtual. A _non-pure_ virtual function is not abstract, nor does it make its class abstract. – Zimano Jan 07 '16 at 11:40
  • @Zimano: which part of my comment is incorrect? I agree that "pure virtual" and "abstract" is the same thing. I was saying that in C++ abstract/pure virtual methods *can* have bodies/implementation and destructors, even abstract, *must* have one. I.e. the snippet ```class A { virtual ~A() = 0 {} }``` is valid – sbk Jan 13 '16 at 12:44
  • 1
    @sbk I get "pure-specifier on function-definition" when I try that – David Doria Mar 17 '16 at 17:19
  • The definition for a pure virtual method must be out-of-line, i.e. `class A { virtual ~A() = 0; }; A::~A() {}` – Oktalist Nov 17 '19 at 19:39
5

Even in an abstract class, you don't want your destructor to be pure virtual. That's because it is going to get called when a concrete subclass' destructor is called.

We use the following pattern.

foo.h

class AbstractBaseClass {
public:
    virtual ~AbstractBaseClass();
    virtual void method1() = 0;
    virtual void method2() = 0;
protected:
    AbstractBaseClass() {
    }
};

foo.cpp

AbstractBaseClass::~AbstractBaseClass() {
}

See this FAQ for details.

MPG
  • 835
  • 4
  • 10
  • You do want it to be pure virtual, you just need to provide an implementation. Also the link you posted does not address pure virtual destructors. –  Mar 31 '10 at 17:50
  • 2
    @MPG: There may be times when you want to declare an abstract base class (one that cannot be instantiated), but you find your self with no virtual functions - you can always declare you destructor pure virtual - then as Neil says it must have an implementation (I'm not sure I realised it either). – quamrana Mar 31 '10 at 18:07
  • 1
    @Neil: if the abstract base class has other pure virtual methods, there's no particular reason for the dtor to be pure as well (as far as I can tell anyway). And as you mention, it's required to have an implementation. Given both of those premises, there's no reason the dtor should be a pure virtual method - and I'd question the rationale for a class where the only pure virtual method was the dtor. – Michael Burr Mar 31 '10 at 18:20
  • @Michael Agreed. Though some might say that if your class is abstract it should always have a PVD - that way if you can add or remove other PVFs without the class ever becoming non-abstract. –  Mar 31 '10 at 18:23
  • But was ConnectionPool::~ConnectionPool defined in a CPP file? Even if you declare it pure virtual, you need a definition. Destructors are odd. The definition should be empty, and I don't think that you want it inlined in the H file. – MPG Mar 31 '10 at 19:22
  • @MPG Have you by any chance bothered to read my answer, a few inches above this? –  Mar 31 '10 at 19:41