online - Problema enlazando métodos "estáticos" en C++
g++ ubuntu (5)
Quiero llamar a algunos métodos "estáticos" de una clase CPP definidos en un archivo diferente, pero estoy teniendo problemas de enlace. Creé un caso de prueba que recrea mi problema y el código para él está debajo.
(Soy completamente nuevo en C ++, vengo de un fondo Java y estoy un poco familiarizado con 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;
}
}
}
Cuando traté de compilar, recibo el siguiente error:
$ 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
Pero si elimino la declaración de clase en main.cpp y la reemplazo con #include "CppClass.cpp", funciona bien. Básicamente, quiero poner estas declaraciones en un archivo .h separado y usarlo. ¿Me estoy perdiendo de algo?
Gracias por la ayuda.
Claro, parece que el vinculador no está recogiendo tu segundo archivo fuente.
Creo que quieres hacer algo como:
g ++ -c CppClass.cpp g ++ -c main.cpp g ++ -o go main.o CppClass.o
Eso debería resolverlo.
Es obvio que proviene de un fondo Java porque aún no ha captado el concepto de archivos de encabezado. En Java, el proceso de definir algo suele ser de una sola pieza. Usted declara y define al mismo tiempo. En C / C ++ es un proceso de dos pasos. Declarar algo le dice al compilador que "algo existe con este tipo, pero más adelante te diré cómo se implementa realmente". Definir algo le está dando al compilador la parte de implementación real. Los archivos de encabezado se usan principalmente para las declaraciones, los archivos .cpp para las definiciones.
Los archivos de encabezado están ahí para describir la "API" de las clases, pero no su código real. Es posible incluir código en el encabezado, que se llama encabezado en línea. Has introducido todo en CppClass.cpp (no es bueno, la alineación del encabezado debe ser la excepción), y luego declaras tu clase en main.cpp OTRA VEZ que es una declaración doble en C ++. La alineación en el cuerpo de la clase conduce a la reduplicación del código cada vez que utiliza un método (esto solo suena a locura. Consulte la sección de preguntas y respuestas de C ++ para obtener detalles).
Incluir la declaración doble en su código le da un error de compilación. Si deja fuera el código de clase, compila pero le da un error de enlazador porque ahora solo tiene la declaración de clase similar a un encabezado en main.cpp. El vinculador no ve ningún código que implemente los métodos de clase, por eso aparecen los errores. A diferencia de Java, el enlazador de C ++ NO buscará automáticamente los archivos de objeto que quiera usar. Si usa la clase XYZ y no le da código objeto para XYZ, simplemente fallará.
Eche un vistazo al artículo del archivo de encabezado de Wikipedia y al Archivo de encabezado Incluir patrones (el enlace también se encuentra en la parte inferior del artículo de Wikipedia y contiene más ejemplos)
En breve:
Para cada clase, genere un archivo NewClass.hy NewClass.cpp.
En el archivo NewClass.h, escribe:
class NewClass {
public:
NewClass();
int methodA();
int methodB();
}; <- don''t forget the semicolon
En el archivo NewClass.cpp, escriba:
#include "NewClass.h"
NewClass::NewClass() {
// constructor goes here
}
int NewClass::methodA() {
// methodA goes here
return 0;
}
int NewClass::methodB() {
// methodB goes here
return 1;
}
En main.cpp, escribe:
#include "NewClass.h"
int main() {
NewClass nc;
// do something with nc
}
Para vincularlo todo, haz un
g ++ -o NewClassExe NewClass.cpp main.cpp
(solo un ejemplo con gcc)
Estás definiendo la clase dos veces, lo cual estoy bastante seguro de que no funciona.
Pruebe algo como esto:
Primero un archivo CppClass.h de cabecera:
// 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();
};
A continuación, un archivo CppClass.cpp que lo implementa:
// 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 */
}
Luego tu archivo principal:
// main.cpp
#include "CppClass.h"
int main()
{
/* main method body here */
}
Creo que la llamada a g ++ sería la misma.
Básicamente, no puedes declarar la misma clase dos veces. Debe declarar la clase en el archivo de encabezado y luego declarar la implementación en el archivo cpp. También podría poner todo el código en línea en una sola declaración de la clase en un archivo de encabezado. Pero declararlo dos veces como lo hiciste no funcionará.
Espero que tenga sentido...
crea un archivo .h con la definición de clase y, a continuación, #incluye ese archivo en tus 2 archivos.