【问题标题】:Linker error: wants C++ virtual base class destructor链接器错误:需要 C++ 虚拟基类析构函数
【发布时间】:2011-02-03 01:04:36
【问题描述】:

我有一个链接错误,链接器抱怨我的具体类的析构函数正在调用它的抽象超类析构函数,其代码丢失。

这是在 XCode 的 Mac OS X 上使用 GCC 4.2。

我看到了g++ undefined reference to typeinfo,但它并不完全相同。

这是链接器错误消息:

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

这是抽象基类声明:

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;
};

这是具体的类声明:

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);
};

不用说,所有这些成员都已实现。这是析构函数:

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

也可能是工厂例程:

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);
}

我可以通过人为地将我的抽象基类具体化来解决这个问题。但我宁愿做一些更好的事情。有什么想法吗?

谢谢

【问题讨论】:

    标签: c++ gcc abstract-class linker-errors


    【解决方案1】:

    即使将析构函数声明为纯虚函数,也必须为其提供实现。尽管您不能直接实例化抽象类,但当您实例化其派生(具体)类之一时,它始终会被实例化。所以在某些时候这样的实例会被销毁,因此需要一个析构函数。纯虚析构函数的实现可以(通常是)一个空函数:

    ConnectionPool::~ConnectionPool() {
    }
    

    【讨论】:

    • 您的回答暗示了这一点,但值得强调的是,在 C++ 中,抽象方法 可以 具有实现。当我发现时,我真的很惊讶,在此之前,我一直认为方法要么是抽象的,要么是有实现的,而不是两者兼而有之。
    • @sbk 这是不正确的。在 C++ 中,抽象意味着 虚拟。 非纯虚函数不是抽象的,也不会使其类抽象。
    • @Zimano:我评论的哪一部分不正确?我同意“纯虚拟”和“抽象”是一回事。我是说在 C++ 中抽象/纯虚拟方法可以 有主体/实现和析构函数,即使是抽象的,必须 有一个。 IE。 sn-p class A { virtual ~A() = 0 {} } 有效
    • @sbk 我在尝试时得到“函数定义上的纯说明符”
    • 纯虚方法的定义必须是外联的,即class A { virtual ~A() = 0; }; A::~A() {}
    【解决方案2】:

    即使在抽象类中,您也不希望您的析构函数是纯虚拟的。这是因为它会在调用具体子类的析构函数时被调用。

    我们使用以下模式。

    foo.h

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

    foo.cpp

    AbstractBaseClass::~AbstractBaseClass() {
    }
    

    看到这个FAQ for details

    【讨论】:

    • 你确实希望它是纯虚拟的,你只需要提供一个实现。您发布的链接也没有解决纯虚拟析构函数。
    • @MPG:有时您可能想要声明一个抽象基类(一个无法实例化的基类),但您会发现自己没有虚函数——您总是可以声明自己的析构函数纯虚函数- 然后正如尼尔所说,它必须有一个实现(我也不确定我是否意识到它)。
    • @Neil:如果抽象基类有其他纯虚方法,那么 dtor 也没有特别的理由是纯的(据我所知)。正如你提到的,它需要有一个实现。鉴于这两个前提,dtor 没有理由应该是纯虚拟方法 - 我会质疑唯一纯虚拟方法是 dtor 的类的基本原理。
    • @Michael 同意。虽然有些人可能会说,如果你的类是抽象的,它应该总是有一个 PVD——如果你可以添加或删除其他 PVF,而不会使类变得非抽象。
    • 但是 ConnectionPool::~ConnectionPool 是在 CPP 文件中定义的吗?即使你声明它是纯虚拟的,你也需要一个定义。析构函数很奇怪。定义应该是空的,我认为你不希望它内联在 H 文件中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-01
    • 2023-02-01
    • 2012-09-01
    • 2015-04-30
    • 2021-04-15
    • 2010-10-15
    • 2017-05-01
    相关资源
    最近更新 更多