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

C++ 未定义对`vtable for XXX的引用(其中XXX是结构异常),c++,multithreading,c++11,thread-safety,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 thr

下面是代码清单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;
}
我有下一个结果:

/tmp/ccqnqlf.o:在函数
empty_stack::empty_stack(empty_stack const&')中:
清单6.1.cpp:(.text.ZN11empty_stackC2ERKS_[_zn1empty_stackC5ERKS_]+0x1d):未定义对
vtable for empty_stack'的引用 /tmp/CCQNQLF.o:(.gcc_除外_表+0x20):对空_堆栈的
typeinfo的未定义引用'
/tmp/ccqnqlf.o:在函数中
empty_stack::empty_stack()': 清单6.1.cpp:(.text.ZN11empty_stackC2Ev[_ZN11empty_stackC5Ev]+0x16):未定义对
vtable for empty_stack的引用'
/tmp/ccqnqlf.o:In function
threadsafe_stack::pop(Msg&)': 清单6.1.cpp:(.text._ZN16threadsafe_stackI3MsgE3popERS0_[_ZN16threadsafe_stackI3MsgE3popERS0_3;]+0x53):对空_堆栈的
typeinfo的未定义引用
/tmp/ccqnqlf.o:在函数中
empty_stack::~empty_stack() 清单6.1.cpp:(.text.ZN11empty_stackD2Ev[ZN11empty_stackD5Ev]+0xb):对“vtable for empty_stack”的未定义引用
collect2:错误:ld返回了1个退出状态

您没有定义
空\u堆栈::什么

因为这意味着您没有定义
empty_stack
的任何成员,而且因为它是多态的(因为
std::exception
有一个虚拟析构函数),这意味着没有为它创建虚拟表,所以您会得到这个看起来很奇怪的错误

如果为
empty_stack
定义了一个虚拟析构函数,则不会得到有关虚拟表的任何错误:

struct empty_stack : std::exception
{
   ~empty_stack() {}
   const char* what() const throw();
};
<>但是,您仍然会收到关于<代码> EMPTYStAs::什么是如果您尝试使用它的链接错误,

< P>从C++ FAQ:

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

()


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

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

在您的情况下,编译器无法找到
public:virtual char const*\uu thiscall empty\u stack::what(void)const“

尝试为空堆栈给出适当的定义::what(void)const
希望这对您有用。

您需要实现

const char* what() const throw();
在空的_堆栈类中

但仍然要小心使用threadsafe_堆栈。 尤其是这个方法是空的。 此方法可能会锁定线程,但在返回堆栈状态后,会释放锁,因此该值无效。 您需要锁定线程并询问状态,并在不释放锁的情况下使用状态

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

您已声明但尚未实现,
empty\u stack::what
Argh”提示“在评论中,答案毫无用处。瘟疫侵袭了我们所有的房子。可能是
const char* what() const throw();