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

StackOverflow https://stackoverflow.com/questions/116687

  •  02-07-2019
  •  | 
  •  

Вопрос

Я хочу вызвать несколько «статических» методов класса 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 и использовать его.Я что-то пропустил?

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

Это было полезно?

Решение

Очевидно, что вы имеете опыт работы с 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)

Другие советы

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top