views:

276

answers:

2

Hi, 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 http://stackoverflow.com/questions/307352/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

+9  A: 

Even if you declare a destructor as a PVF, 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() {
}
anon
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
+2  A: 

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
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.
anon
@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
@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
@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.
anon
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
@MPG Have you by any chance bothered to read my answer, a few inches above this?
anon