8

我想调用在不同文件中定义的 CPP 类的一些“静态”方法,但我遇到了链接问题。我创建了一个重新创建我的问题的测试用例,它的代码如下。

(我对 C++ 完全陌生,我来自 Java 背景,对 C 有点熟悉。)

// CppClass.cpp
#include <iostream>
#include <pthread.h>

static pthread_t thread;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static int shutdown;

using namespace std;

class CppClass
{
public:
        static void Start()
        {
                cout << "Testing start function." << endl;
                shutdown = 0;
                pthread_attr_t attr;
                pthread_attr_init(&attr);
                pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
                pthread_mutex_init(&mutex, NULL);
                pthread_cond_init(&cond, NULL);

                pthread_create(&thread, &attr, run_thread, NULL);
        }

        static void Stop()
        {
                pthread_mutex_lock(&mutex);
                shutdown = 1;
                pthread_cond_broadcast(&cond);
                pthread_mutex_unlock(&mutex);
        }

        static void Join()
        {
                pthread_join(thread, NULL);
        }
private:
        static void *run_thread(void *pthread_args)
        {
                CppClass *obj = new CppClass();
                pthread_mutex_lock(&mutex);
                while (shutdown == 0)
                {
                        struct timespec ts;
                        ts.tv_sec = time(NULL) + 3;
                        pthread_cond_timedwait(&cond, &mutex, &ts);
                        if (shutdown)
                        {
                                break;
                        }
                        obj->display();
                }
                pthread_mutex_unlock(&mutex);
                pthread_mutex_destroy(&mutex);
                pthread_cond_destroy(&cond);
                pthread_exit(NULL);
                return NULL;
        }

        void display()
        {
                cout << " Inside display() " << endl;
        }
};

// main.cpp
#include <iostream>
/* 
 * If I remove the comment below and delete the
 * the class declaration part, it works.
 */
// #include "CppClass.cpp"
using namespace std;

class CppClass
{
public:
        static void Start();
        static void Stop();
        static void Join();
};

int main()
{
        CppClass::Start();
        while (1)
        {
                int quit;
                cout << "Do you want to end?: (0 = stay, 1 = quit) ";
                cin >> quit;
                cout << "Input: " << quit << endl;
                if (quit)
                {
                        CppClass::Stop();
                        cout << "Joining CppClass..." << endl;
                        CppClass::Join();
                        break;
                }
        }
}

当我尝试编译时,出现以下错误:

$ g++ -o go main.cpp CppClass.cpp -l pthread
/tmp/cclhBttM.o(.text+0x119):在函数“main”中:
: 未定义对 `CppClass::Start()' 的引用
/tmp/cclhBttM.o(.text+0x182):在函数“main”中:
: 未定义对 `CppClass::Stop()' 的引用
/tmp/cclhBttM.o(.text+0x1ad):在函数“main”中:
: 未定义对 `CppClass::Join()' 的引用
collect2: ld 返回 1 个退出状态

但是,如果我删除 main.cpp 中的类声明并将其替换为 #include "CppClass.cpp",它就可以正常工作。基本上,我想将这些声明放在一个单独的 .h 文件中并使用它。我错过了什么吗?

谢谢您的帮助。

4

5 回答 5

33

很明显您来自 Java 背景,因为您还没有掌握头文件的概念。在 Java 中,定义某些东西的过程通常是一体的。您同时声明和定义。在 C/C++ 中,这是一个两步过程。声明一些东西会告诉编译器“这种类型存在一些东西,但我稍后会告诉你它是如何实际实现的”。定义一些东西是给编译器实际的实现部分。头文件主要用于声明,.cpp 文件用于定义。

头文件用于描述类的“API”,但不是它们的实际代码。可以在标头中包含代码,这称为标头内联。您已经内联了 CppClass.cpp 中的所有内容(不好,标题内联应该是例外),然后您在 main.cpp AGAIN 中声明您的类,这是 C++ 中的双重声明。每次使用方法时,类主体中的内联都会导致代码重复(这听起来很疯狂。有关详细信息,请参阅有关内联的 C++ 常见问题解答部分。)

在你的代码中包含双重声明会给你一个编译器错误。保留类代码编译但会给您一个链接器错误,因为现在您在 main.cpp 中只有类似标题的类声明。链接器看不到实现您的类方法的代码,这就是出现错误的原因。与 Java 不同,C++ 链接器不会自动搜索它想要使用的目标文件。如果你使用 XYZ 类并且不给它 XYZ 的目标代码,它只会失败。

请查看维基百科的头文件文章头文件包含模式(链接也在维基百科文章的底部,包含更多示例)

简而言之:

对于每个类,生成一个 NewClass.h 和 NewClass.cpp 文件。

在 NewClass.h 文件中,写入:

class NewClass {
public:
   NewClass();
   int methodA();
   int methodB();
}; <- don't forget the semicolon

在 NewClass.cpp 文件中,写入:

#include "NewClass.h"

NewClass::NewClass() {
  // constructor goes here
}

int NewClass::methodA() {
  // methodA goes here
  return 0;
}

int NewClass::methodB() {
  // methodB goes here
  return 1;
}

在 main.cpp 中,编写:

#include "NewClass.h"

int main() {
  NewClass nc;
  // do something with nc
}

要将它们链接在一起,请执行

g++ -o NewClassExe NewClass.cpp main.cpp

(只是 gcc 的一个例子)

于 2008-09-22T18:51:39.710 回答
10

你定义了两次类,我很确定这不起作用。

尝试这样的事情:

首先是头文件 CppClass.h 文件:

// CppClass.h
using namespace std;

class CppClass
{
public:
    static void Start();
    static void Stop();
    static void Join();
private:
    void *run_thread(void *pthread_args);
    void display();
};

然后是实现它的 CppClass.cpp 文件:

// CppClass.cpp
#include <iostream>
#include <pthread.h>
#include "CppClass.h"

using namespace std;

void CppClass::Start()
{
    /* method body goes here */
}
void CppClass::Stop()
{
    /* method body goes here */
}
void CppClass::Join()
{
    /* method body goes here */
}
void *CppClass::run_thread(void *pthread_args)
{
    /* method body goes here */
}
void CppClass::display() {
    /* method body goes here */
}

然后你的主文件:

// main.cpp
#include "CppClass.h"

int main()
{
    /* main method body here */
}

我相信 g++ 调用会是一样的。

基本上,你不能两次声明同一个类。您应该在头文件中声明类,然后在 cpp 文件中声明实现。您还可以将所有代码内联放在头文件中的类的单个声明中。但是像你一样声明两次是行不通的。

我希望这是有道理的...

于 2008-09-22T19:16:00.510 回答
2

我想你想做这样的事情:

g++ -c CppClass.cpp g++ -c main.cpp g++ -o go main.o CppClass.o

那应该解决它。

于 2008-09-22T18:45:29.897 回答
1

制作一个包含类定义的 .h 文件,然后将该文件 #include 到您的 2 个文件中。

于 2008-09-22T18:48:47.797 回答
0

当然看起来链接器没有选择你的第二个源文件。

于 2008-09-22T18:51:28.587 回答