【问题标题】:unresolved external Symbol (Constructor)未解析的外部符号(构造函数)
【发布时间】:2014-04-20 07:21:30
【问题描述】:

在构建时出现以下错误:

main.obj:错误 LNK2019:函数“main”中未解析的外部符号“”public:__cdecl Worker::Worker(void)“(??0Worker@@QEAA@XZ)”。

main.obj:错误 LNK2019:函数“main”中无法解析的外部符号“”public:virtual __cdecl Worker::~Worker(void)”(??1Worker@@UEAA@XZ)”。

我找不到问题。 (我也看了here

ma​​in.cpp

#include <iostream>
#include <thread>
#include "worker.h"

using namespace std;

void pause_thread(int n)
{
    std::this_thread::sleep_for (std::chrono::seconds(n));
    std::cout << "pause of " << n << " seconds ended\n";
}

int main()
{
    std::cout << "Spawning and detaching 3 threads...\n";
    std::thread (pause_thread,1).detach();
    std::thread (pause_thread,2).detach();
    std::thread (pause_thread,3).detach();
    std::cout << "Done spawning threads.\n";

    std::cout << "(the main thread will now pause for 5 seconds)\n";
    // give the detached threads time to finish (but not guaranteed!):
    pause_thread(5);

    Worker w;



    return 0;
}

worker.h

#ifndef WORKER_H
#define WORKER_H


#include "jobqueue.h"
#include "job.h"
#include <mutex>
#include <thread>

using namespace std;

class Worker
{
private:
    JobQueue jobs;
    mutex mu;
    thread & workerThread;
    bool stop;

    void work();

public:
    Worker();
    virtual ~Worker();
    void addJob(Job*);
    int getJobCount();


};

#endif // WORKER_H

worker.cpp

#include "worker.h"

Worker::Worker(): workerThread(work), stop(false)
{
}

Worker::~Worker()
{
    workerThread.join();
}

void Worker::work(){
    while (!stop) {
        unique_lock<mutex> lock(mu, defer_lock);
        lock.lock();
            Job* job = jobs.getNextJob();
        lock.unlock();

        job->run();
        delete job;
    }
}

void Worker::addJob(Job* job){
    jobs.append(job);
}

int Worker::getJobCount(){
    unique_lock<mutex> lock(mu);
    return jobs.size();
}

project.pro

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp \
    jobqueue.cpp \
    worker.cpp

HEADERS += \
    jobqueue.h \
    worker.h \
    job.h

删除 Project.pro.user 解决了(主要)问题,现在再次显示错误

【问题讨论】:

  • 您在链接文件吗? stackoverflow.com/questions/12573816/…
  • 您必须提供所有目标文件以进行链接。您可能错过了包含 worker.o 目标文件。
  • 作为 IDE,我使用 QT Creator 和 MSVC 2012 64Bit 作为编译器。我将在上面发布我的项目文件。
  • C++ 类的构造函数和解构函数的调用约定是 __thiscall,而不是 __cdecl。很难猜出你是怎么做到的。
  • 删除 project.pro.user 文件后出现编译错误。 @Manu343726 我正在学习 C++。我从 Java 切换过来,所以我不是 C++ 专家。在 C++ 中,您必须了解 Java 中的所有错误,编译器具有更好的错误检测能力。顺便说一句,如果我想让线程调用 Worker::work() 方法,最好的方法是什么?

标签: c++ lnk2019


【解决方案1】:

您的代码中有很多错误,我建议您在此之前应该学习更多 C++ 基础知识。
正如我在我的 cmets 中显示的错误,让我只用成员函数来回答你的问题:

C++ 将函数视为一等公民,而不是 Java(Java 8 的改进修复了一些问题,但它也不将函数视为一等公民)。
C++ 将函数理解为 可调用实体 的概念:可调用实体是可以调用的任何东西,即视为函数。所以一个可调用的实体可能是:

  • 全局函数:只是很好的旧 C 函数。它可以被定义、实现和调用:

    void f() {}
    
    int main()
    {
        f(); //Call to f
    }
    
  • 成员函数:经典的 OO 成员函数。它在对象中调用,并对其数据进行操作:

    struct foo
    {
        void f();   
    };
    
    int main()
    {
        foo myfoo;
    
        myfoo.f(); //Call to foo::f
    }
    
  • 静态成员函数:它是一个不链接到对象的成员函数,它在类级别运行,所以它的签名与全局函数的签名相同 > (记住这一点,它很重要,我们稍后会看到)

    struct foo
    {
        static void f();
    {
    
    int main()
    {
        foo::f(); //Call to foo::f
    }
    
  • 仿函数:仿函数只是一个类,其对象被设计为像函数一样工作。这就实现了 () 运算符的重载:

    struct f
    {
        void operator()() const
        {}
    };
    
    int main()
    {
        f myf;
    
        myf(); //Call to foo
    }
    

    标准库定义了模板std::function,这是一个类型擦除函子,旨在保存任何类型的可调用实体:

    #include <functional>
    
    void f() {}
    
    int main()
    {
        std::function<void()> f_wrapper;
    
        f_wrapper(); //Call to f_wrapper, which is an indirect call to f
    } 
    
  • lambda 表达式: lambda 表达式只是一个就地定义的匿名函子:

    int main()
    {
        std::function<void()> lambda = [](){ std::cout << "hello!"; };
    }
    

    你好!

  • 指向函数的指针: C 允许用户通过函数指针存储函数,就像它允许存储指向数据的指针一样。 C++ 具有相同的功能,也扩展了成员函数:

    void f() {}
    void g( void(*function)() ) 
    { 
        function(); //Call to the function referenced by the pointer passed as parameter 
    }
    
    int main()
    {
        g(f); //Call to g passing f as parameter. Its an indirect call to f. Note that the & is not needed
    }
    

    正如我们上面所说,静态成员函数与全局函数具有相同的签名,因此语法与上面的示例完全相同。
    但是对于成员函数就不一样了:成员函数链接到一个对象,所以它在一个对象中被调用。成员函数指针的语法如下:

    struct foo
    {
        voif f();
    };
    
    typedef void(foo::* pointer_to_f_type)();
    
    int main()
    {
        pointer_to_f_pointer ptr = &foo::f; //Note that the & is needed, just like in variable pointers
        foo myfoo;
    
        (myfoo.*ptr)(); //Call to the foo member function pointed by ptr (foo::f) using myfoo as object
    }
    

    特定签名的成员函数指针与指向相同的全局/静态函数的指针无关 签名,并且不能从/到成员指针转换为非成员指针,反之亦然

    因为函数指针和成员函数指针是完全分离的东西,我们不能对待任何一种函数 例如,我们不能创建一个同时包含函数指针和成员函数指针的数组。 但是,标准库提供了函数模板std::bind,它允许我们将函数绑定到一些(或全部)调用参数。 也就是说,std::bind() 返回的对象表示对可调用实体的部分(或完整)调用
    例如:

     void f( int , int , int ) {}
    
     int main()
     {
         std::function<void(int,int,int)> f_wrapper = f;
    
         f(1,2,3); //Ok
         f_wrapper(1,2,3); //Ok
    
         std::function<void()> f_call = std::bind( f , 1 , 2 , 3 ); //f_call represents a partial call (Complete in this case) fo f
         f_call(); //Execute the call
    
         std::function<void(int)> partial_f_call = std::bind( f , std::placeholders::_1 , 2 , 3 );
         partial_f_call( 1 ); //Same execution as above
     } 
    

    如您所见,std::bind() 允许我们将某些参数绑定到函数,从而创建一个可调用的实体 表示对函数的调用。所以 它可以用来将一个对象绑定到一个成员函数上,形成一个可调用的实体 与使用任何其他类型的可调用实体初始化的 std::function 实例的形式完全相同。 我们可以使用std::function以相同的方式存储成员和非成员函数,并以相同的方式使用它**:

     void f();
    
     struct foo
     {
         void f();
     };
    
     int main()
     {
         std::vector<std::function<void()>> functions;
         foo myfoo;
    
         functions.push_back( f );
         functions.push_back( std::bind( &foo::f , myfoo ) );
         functions.push_back( [](){} );
         ...
    
         for( const auto& function : functions )
             function();
     }
    

如您所见,可调用实体有多种形式。重要的一点是有人可以使用 C++ 模板并依靠鸭子类型来使用可调用的 作为参数传递的实体:

 template<typename F>
 void call_function( const F& function )
 {
     function(); //function should be any kind of thing which could be called, that is, a callable entity
 } 

这正是std::thread 构造函数的作用。它只需要任何类型的可调用实体,一组调用参数,启动一个新线程, 然后在该新线程上调用可调用实体(通过join()detach()。它的实现可能类似于:

template<typename F , typename... ARGS>
thread::thread( F&& function , ARGS&&... args )
{
    _thread = create_thread();

    _function = std::bind( std::forward<F>( function ) , std::forward<ARGS>( args )... ); 
}


void thread::detach()
{
    detach_thread( _thread );
    _function();
}

void thread::join()
{
    join_thread( _thread );
    _function();
}

当然,这不是一个有效的实现,只是一个概述:p

所以现在您可以理解为什么您的方法不起作用了,以及您可以做些什么来解决这个问题。具体来说,使用std::bind() 创建一个可调用实体 如果你想在线程上使用成员函数

【讨论】:

  • 感谢您的工作!这个话题对我来说仍然有点神秘。在编译器给我实际错误后,我解决了我的问题。我的解决方案仍然比 C++ 风格更 Java 风格,但它现在可以工作。我会根据你的建议重构我的代码,谢谢。
  • @niccomatik 我知道对于 C++ 新手来说,这篇文章的信息太多了。我只是想向你展示你犯错的原因。我建议您阅读一本好的 C++ 书籍(查看本网站的 C++FAQ)并沿着 SO 搜索有关该语言最重要主题的好问题/答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
  • 2019-08-15
  • 1970-01-01
  • 2013-11-03
  • 2013-03-04
  • 1970-01-01
相关资源
最近更新 更多