Pregunta

Quiero llamar a algunos métodos "estáticos" de una clase CPP definida en un archivo diferente pero tengo problemas de vinculación.Creé un caso de prueba que recrea mi problema y el código se encuentra a continuación.

(Soy completamente nuevo en C++, tengo experiencia en 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 intenté compilar, aparece 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.

¿Fue útil?

Solución

Es obvio que tiene experiencia en Java porque aún no ha comprendido el concepto de archivos de encabezado.En Java, el proceso de definir algo suele ser de una sola pieza.Declaras y defines al mismo tiempo.En C/C++ es un proceso de dos pasos. declarando algo le dice al compilador "algo existe con este tipo, pero más adelante le diré cómo se implementa realmente". Definiendo algo le está dando al compilador la parte de implementación real.Los archivos de encabezado se utilizan principalmente para declaraciones, los archivos .cpp para 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, lo que se denomina inserción de encabezado.Ha incluido todo en CppClass.cpp (no es bueno, la inclusión de encabezado debería ser la excepción) y luego declara su clase en main.cpp OTRA VEZ, que es una declaración doble en C++.La inserción en el cuerpo de la clase conduce a la reduplicación del código cada vez que usa un método (esto solo sonidos loco.Ver el Sección de preguntas frecuentes de C++ sobre inserción para detalles.)

Incluir la doble declaración en su código le genera un error de compilación.Dejar el código de la clase fuera se compila pero genera un error del vinculador 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 su clase, por eso aparecen los errores.A diferencia de Java, el vinculador de C++ NO buscará automáticamente los archivos objeto que desee utilizar.Si usa la clase XYZ y no le da código objeto para XYZ, simplemente fallará.

Por favor echa un vistazo a Artículo del archivo de encabezado de Wikipedia y El archivo de encabezado incluye patrones (el enlace también se encuentra al final del artículo de Wikipedia y contiene más ejemplos)

En breve:

Para cada clase, genere un archivo NewClass.h y NewClass.cpp.

En el archivo NewClass.h, escriba:

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, escriba:

#include "NewClass.h"

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

Para vincularlo todo, haga un

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

(solo un ejemplo con gcc)

Otros consejos

Estás definiendo la clase dos veces, lo cual estoy bastante seguro de que no funciona.

Pruebe algo como esto:

Primero, un archivo de encabezado 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();
};

Luego 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 de 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 puedes poner todo el código en línea en un soltero declaración de la clase en un archivo de encabezado.Pero declararlo dos veces como lo hizo no funcionará.

Espero que tenga sentido...

Creo que quieres hacer algo como:

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

Eso debería resolverlo.

cree un archivo .h con la definición de clase y luego #incluya ese archivo en sus 2 archivos.

Seguro que parece que el vinculador no selecciona su segundo archivo fuente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top