未定义的引用`vtable for XXX(其中XXX是结构异常)

时间:2015-02-09 14:32:46

标签: c++ multithreading c++11 thread-safety

我有下一个代码列表-6.1.cpp:

#include <stack>
#include <mutex>
#include <memory>
#include <future>
#include <iostream>
#include <exception>
using namespace std;

struct empty_stack: std::exception
{
const char* what() const throw();
};


template<typename T>
class threadsafe_stack
{
private:
    std::stack<T> data;
    mutable std::mutex m;
public:
    threadsafe_stack(){}
    threadsafe_stack(const threadsafe_stack& other)
    {
        std::lock_guard<std::mutex> lock(other.m);
        data=other.data;
    }
    threadsafe_stack& operator=(const threadsafe_stack&) = delete;
    void push(T new_value)
    {
        std::lock_guard<std::mutex> lock(m);
        data.push(std::move(new_value));
    }
    std::shared_ptr<T> pop()
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        std::shared_ptr<T> const res(
            std::make_shared<T>(std::move(data.top())));
        data.pop();
        return res;
    }
    void pop(T& value)
    {
        std::lock_guard<std::mutex> lock(m);
        if(data.empty()) throw empty_stack();
        value=std::move(data.top());
        data.pop();
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(m);
        return data.empty();
    }
};


struct Msg {
    size_t a;size_t b;size_t c;size_t d;
};

bool isCorrupted(const Msg& m) {
    return !(m.a == m.b && m.b == m.c && m.c == m.d);
}




int main()
{
    threadsafe_stack<Msg> stack;

    auto prod = std::async(std::launch::async, [&]() {
        for (size_t i = 0; i < 1000000; ++i){
            Msg m = { i, i, i, i };
            stack.push(m);
            //std::this_thread::sleep_for(std::chrono::microseconds(1));
            if (i % 1000 == 0) {
                std::cout << "stack.push called " << i << " times " << std::endl;
            }
        }
    });

    auto cons = std::async(std::launch::async, [&]() {
        for (size_t i = 0; i < 1000000; ++i){
            try {
                Msg m;
                stack.pop(m);
                if (isCorrupted(m)) {
                    std::cout << i <<" ERROR: MESSAGE WAS CORRUPED:" << m.a << "-" << m.b << "-" << m.c << "-" << m.d << std::endl;
                }
                if (i % 1000 == 0) {
                    std::cout << "stack.pop called " << i << " times " << std::endl;
                }
            }
            catch (empty_stack e) {
                std::cout << i << " Stack was empty!" << std::endl;
            }
        }
    });

    prod.wait();
    cons.wait();

    return 0;
}

用以下代码编译后:

g++ -o exe.out listing-6.1.cpp -pthread -lboost_system -lboost_thread -std=c++0x

我有下一个结果:

/tmp/ccqQnqlf.o:函数empty_stack::empty_stack(empty_stack const&)': listing-6.1.cpp:(.text._ZN11empty_stackC2ERKS_[_ZN11empty_stackC5ERKS_]+0x1d): undefined reference to vtable for empty_stack' /tmp/ccqQnqlf.o:(.gcc_except_table+0x20):对typeinfo for empty_stack' /tmp/ccqQnqlf.o: In function empty_stack :: empty_stack()'的未定义引用: listing-6.1.cpp :(。text._ZN11empty_stackC2Ev [_ZN11empty_stackC5Ev] + 0x16):对vtable for empty_stack' /tmp/ccqQnqlf.o: In function threadsafe_stack :: pop(Msg&amp;)'的未定义引用: listing-6.1.cpp :(。text._ZN16threadsafe_stackI3MsgE3popERS0 _ [_ ZN16threadsafe_stackI3MsgE3popERS0 _] + 0x53):对typeinfo for empty_stack' /tmp/ccqQnqlf.o: In function empty_stack ::〜empty_stack()'的未定义引用: listing-6.1.cpp :(。text._ZN11empty_stackD2Ev [_ZN11empty_stackD5Ev] + 0xb):未定义引用`vtable for empty_stack' collect2:错误:ld返回1退出状态

4 个答案:

答案 0 :(得分:1)

您没有定义empty_stack::what

因为这意味着您没有定义 empty_stack的任何成员,并且因为它是多态的(因为std::exception有一个虚拟析构函数),这意味着没有虚拟表是为它创建的,所以你会得到这个特殊的错误。

如果您为empty_stack定义了虚拟析构函数,那么您将不会收到有关虚拟表的任何错误:

struct empty_stack : std::exception
{
   ~empty_stack() {}
   const char* what() const throw();
};

但如果您尝试使用empty_stack::what,则仍会收到有关{{1}}的链接器错误。

答案 1 :(得分:1)

在大多数情况下,LNK2001由于定义缺失而发生未定义的引用链接器错误。!

在您的情况下,编译器无法找到public: virtual char const * __thiscall empty_stack::what(void)const "

的定义

尝试给empty_stack::what(void)const提供适当的定义。希望这对您有用。

答案 2 :(得分:0)

来自C ++ FAQ:

  

许多编译器将这个神奇的“虚拟表”放在编译单元中,该单元定义了类中的第一个非内联虚函数。因此,如果Fred中的第一个非内联虚函数是wilma(),编译器会将Fred的虚拟表放在它看到Fred :: wilma()的同一个编译单元中。不幸的是,如果您不小心忘记定义Fred :: wilma(),而不是未定义Fred :: wilma(),您可能会得到“Fred的虚拟表未定义”。伤心但真实。

http://www.parashift.com/c++-faq/link-errs-missing-vtable.html

因此,您需要定义empty_stack::what

答案 3 :(得分:0)

您需要实施

const char* what() const throw();
<_>在empty_stack类中。

但是,请小心使用你的threadsafe_stack。 特别是方法空了。 此方法可能是锁定线程,但在返回堆栈状态后,锁定被释放,因此该值无用。 您需要锁定线程并询问状态并使用状态而不释放锁定。

实施例: 线程A即将弹出一个值,但是线程B要求空状态。 B很高兴堆栈不为空,但同时A将弹出最后一项并且堆栈为空。 B现在手中的状态无效。