Проблема связывания «статических» методов в 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 и использовать его.Я что-то пропустил?
Спасибо за помощь.
Решение
Очевидно, что вы имеете опыт работы с 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 файла.
Конечно, похоже, что компоновщик не подбирает второй исходный файл.