سؤال

أرغب في استدعاء بعض الطرق "الثابتة" لفئة 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

(مجرد مثال مع دول مجلس التعاون الخليجي)

نصائح أخرى

أنت تحدد الفصل مرتين، وأنا متأكد من أنه لن ينجح.

جرب شيئًا مثل هذا:

أولاً ملف رأس 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 مع تعريف الفئة فيه، ثم قم بتضمين هذا الملف في الملفين الخاصين بك.

يبدو بالتأكيد أن الرابط لا يلتقط الملف المصدر الثاني.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top