Проблема связывания статических методов в C ++

Я хочу вызвать несколько «статических» методов класса 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): In function `main':
: undefined reference to `CppClass::Start()'
/tmp/cclhBttM.o(.text+0x182): In function `main':
: undefined reference to `CppClass::Stop()'
/tmp/cclhBttM.o(.text+0x1ad): In function `main':
: undefined reference to `CppClass::Join()'
collect2: ld returned 1 exit status

Но если я удалю объявление класса в main.cpp и заменю его на #include «CppClass.cpp», он будет работать нормально. По сути, я хочу поместить эти объявления в отдельный файл .h и использовать его. Я что-то упускаю?

Спасибо за помощь.


person Srikanth    schedule 22.09.2008    source источник
comment
Поместите свой код в блок ‹pre›   -  person Adam Rosenfield    schedule 22.09.2008


Ответы (5)


Очевидно, что вы пришли из фона Java, потому что вы еще не поняли концепцию файлов заголовков. В Java процесс определения чего-либо обычно неразрывно связан. Вы заявляете и определяете одновременно. В C / C ++ это двухэтапный процесс. Объявление чего-либо сообщает компилятору, что «что-то существует с этим типом, но я расскажу вам позже, как это на самом деле реализовано». Определение чего-либо дает компилятору фактическую часть реализации. Заголовочные файлы используются в основном для объявлений, файлы .cpp - для определений.

Заголовочные файлы предназначены для описания «API» классов, но не для их фактического кода. В заголовок можно включить код, который называется вставкой заголовка. Вы встроили все в CppClass.cpp (это нехорошо, встраивание заголовков должно быть исключением), а затем вы снова объявляете свой класс в main.cpp, что является двойным объявлением в 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)

person Community    schedule 22.09.2008

Вы определяете класс дважды, что, я уверен, не сработает.

Попробуйте что-то вроде этого:

Сначала файл заголовка 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. Вы также можете встроить весь код в единственное объявление класса в файле заголовка. Но объявить это дважды, как вы, не сработает.

Надеюсь, это имело смысл ...

person Herms    schedule 22.09.2008

Я думаю, вы хотите сделать что-то вроде:

g ++ -c CppClass.cpp g ++ -c main.cpp g ++ -o перейти на main.o CppClass.o

Это должно решить эту проблему.

person Jon    schedule 22.09.2008

создайте файл .h с определением класса в нем, а затем #include этот файл в ваши 2 файла.

person Greg Rogers    schedule 22.09.2008

Конечно, похоже, что компоновщик не забирает второй исходный файл.

person Benoit    schedule 22.09.2008