2013-10-04 29 views
0

我很難將我編寫的C++類與單獨的「測試」C++文件鏈接起來。所以我有這三個文件:threadtest.cc,Elevator.h和Elevator.cc。在Elevator.h中,我定義了兩個類:Passenger和Elevator。當我嘗試從threadtest.cc文件引用Passenger或Elevator的函數時,出現未定義的引用錯誤。我做的第一件事是確保我在threadtest.cc中有一個include指令。我一直試圖在各處搜索鏈接和NACHOS類的指南,但我唯一能找到的是NACHOS pdf路線圖,它缺少這些問題出現時所需的詳細信息。引用另一個文件中的C++類

我正在包含來自Elevator.h,threadtest.cc和Makefile的部分片斷,它們可能與這個鏈接問題最相關。

下面是我最關心的是在編譯時錯誤:

threadtest.o(.text+0x1c4):../threads/threadtest.cc:103: undefined reference to `Passenger::FromFloor()' 
threadtest.o(.text+0x1d3):../threads/threadtest.cc:103: undefined reference to `Passenger::ToFloor()' 
threadtest.o(.text+0x1e2):../threads/threadtest.cc:103: undefined reference to `Passenger::ID()' 
threadtest.o(.text+0x204):../threads/threadtest.cc:104: undefined reference to `Elevator::Request(Passenger*)' 
threadtest.o(.text+0x215):../threads/threadtest.cc:106: undefined reference to `Passenger::ID()' 
threadtest.o(.text+0x237):../threads/threadtest.cc:107: undefined reference to `Elevator::LoadPassenger(Passenger*)' 
threadtest.o(.text+0x248):../threads/threadtest.cc:109: undefined reference to `Passenger::ID()' 
threadtest.o(.text+0x26a):../threads/threadtest.cc:110: undefined reference to `Elevator::UnloadPassenger(Passenger*)' 

這裏是有問題的線路從threadtest.cc起始線97 T0 111:

#include "Elevator.h" 

class Passenger; 
class Elevator; 

void  
RunPassenger(int ptr) 
{ 
    int *temp = (int*) ptr; 
    Passenger *P = new Passenger(temp[ 0 ], temp[ 1 ], temp[ 2 ]); 

    printf("Person %d wants to go to floor %d from floor %d\n", P->ID(), P->ToFloor(), P->FromFloor()); 
    E->Request(P); 

    printf("Person %d got into the elevator\n", P->ID()); 
    E->LoadPassenger(P); 

    printf("Person %d got out of the elvator\n", P->ID()); 
    E->UnloadPassenger(P); 
} 

這裏我在Elevator.h中的類的定義

#ifndef ELEVATOR_H 
#define ELEVATOR_H 

#include "copyright.h" 
#include "thread.h" 
#include "list.h" 
#include "synch.h" 

class Passenger 
{ 
    private: 
    int fromFloor; 
    int toFloor; 
    int tID; 
    bool passengerDirection; 

    public: 
    Passenger(int fromFloor, int toFloor, int tID); 
    int FromFloor(); 
    int ToFloor(); 
    int ID(); 
    bool pDirection(); 
}; 

class Elevator 
{ 
    public: 
    Elevator(); 
    Elevator(int numFloors); 
    ~Elevator(); 
    int TotalFloors(int numFloors); 
    Thread* ThreadPtr(); 
    bool IsAtCapacity(); 
    bool PassengerDirection(bool direction); 
    void LoadPassenger(Passenger *P); 
    void UnloadPassenger(Passenger *P); 
    void PassengersWaiting(); 
    void Run(); 
    void Initialize(); 
    void AddToQueue(Passenger *P); 
    void Request(Passenger *P); 

    private: 
    Thread *T; 
    List *Queue, *UpQueue, *DownQueue; 
    Semaphore *S; 
    int NumFloors; 
    int* PassCounter; 
    int OnBoard; 
    bool Direction; 
    int CurrentFloor; 
    Condition *ElevatorCV; 
    Condition *PassengerCV; 
    Lock *lock; 
}; 

#endif 

最後,還有一個GNU Make該文件用於編譯NACHOS中的所有內容。這裏是Makefile中對應threadtest.cc和Elevator.h

threadtest.o: ../threads/threadtest.cc ../threads/copyright.h \ 
    ../threads/system.h ../threads/utility.h ../threads/bool.h \ 
    ../threads/Elevator.h ../threads/Elevator.cc \ 
    ../machine/sysdep.h ../threads/copyright.h /usr/include/stdio.h \ 
    /usr/include/features.h /usr/include/sys/cdefs.h \ 
    /usr/include/gnu/stubs.h \ 
    /usr/lib/gcc/i386-redhat-linux/3.4.6/include/stddef.h \ 
    /usr/include/bits/types.h /usr/include/bits/wordsize.h \ 
    /usr/include/bits/typesizes.h /usr/include/libio.h \ 
    /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \ 
    /usr/include/gconv.h ../threads/stdarg.h /usr/include/bits/stdio_lim.h \ 
    /usr/include/bits/sys_errlist.h /usr/include/string.h \ 
    /usr/include/xlocale.h ../threads/thread.h ../threads/scheduler.h \ 
    ../threads/list.h ../machine/interrupt.h ../threads/list.h \ 
    ../machine/stats.h ../machine/timer.h ../threads/utility.h 

Elevator.o: ../threads/Elevator.cc ../threads/threadtest.h ../threads/copyright.h \ 
    ../threads/utility.h ../threads/system.h ../threads/utility.h ../threads/bool.h \ 
    ../machine/sysdep.h /usr/include/stdio.h \ 
    /usr/include/features.h /usr/include/sys/cdefs.h \ 
    /usr/include/gnu/stubs.h \ 
    /usr/lib/gcc/i386-redhat-linux/3.4.6/include/stddef.h \ 
    /usr/include/bits/types.h /usr/include/bits/wordsize.h \ 
    /usr/include/bits/typesizes.h /usr/include/libio.h \ 
    /usr/include/_G_config.h /usr/include/wchar.h /usr/include/bits/wchar.h \ 
    /usr/include/gconv.h ../threads/stdarg.h /usr/include/bits/stdio_lim.h \ 
    /usr/include/bits/sys_errlist.h /usr/include/string.h \ 
    /usr/include/xlocale.h ../threads/thread.h ../threads/scheduler.h \ 
    ../threads/list.h ../machine/interrupt.h ../threads/list.h \ 
    ../machine/stats.h ../machine/timer.h ../threads/utility.h 

這裏的部分的所有功能定義在Elevator.cc

#include "Elevator.h" 
#include "thread.h" 
#include "Condition.h" 
#include "synch.h" 

#define MAX_CAPACITY 5 

//---------------------------------------------------------------------- 
// Elevator::Elevator 
// Initialize an elevator, without any parameters 
//---------------------------------------------------------------------- 
Elevator::Elevator() 
{ 
    T = new Thread("Elevator"); 
    S = new Semaphore("Semaphore", 1); 
    Queue = new List; UpQueue = new List; DownQueue = new List; 
    OnBoard = 0; 
    CurrentFloor = 0; 
} 

//---------------------------------------------------------------------- 
// Elevator::Elevator 
// Initialize an elevator. 
// 
// "NumFloors" is the total number of floors an elevator will traverse 
// "T" is a new Thread for an elevator 
// "S" is a Semaphore that is used for synchronization of elevator 
// "Queue" is a List that holds Passengers in the elevator 
// "UpQueue" and "DownQueue" hold Passengers waiting for elevator 
// "OnBoard" is a counter of the Passengers on the elevator 
// "CurrentFloor" is a counter of the floors the elevator has visited 
//---------------------------------------------------------------------- 
Elevator::Elevator(int numFloors) : NumFloors(numFloors) 
{ 
    T = new Thread("Elevator"); 
    S = new Semaphore("Semaphore", 1); 
    Queue = new List; UpQueue = new List; DownQueue = new List; 
    OnBoard = 0; 
    CurrentFloor = 0; 
} 

//---------------------------------------------------------------------- 
// Elevator::Elevator 
// Initialize an elevator. 
// 
// "NumFloors" is the total number of floors an elevator will traverse 
// "T" is a new Thread for an elevator 
// "S" is a Semaphore that is used for synchronization of elevator 
// "Queue" is a List that holds Passengers in the elevator 
// "UpQueue" and "DownQueue" hold Passengers waiting for elevator 
// "OnBoard" is a counter of the Passengers on the elevator 
// "CurrentFloor" is a counter of the floors the elevator has visited 
//---------------------------------------------------------------------- 
Elevator::~Elevator() 
{ 
    delete T; 
    delete S; 
    delete Queue; 
    delete UpQueue; 
    delete DownQueue; 
} 

//---------------------------------------------------------------------- 
// Elevator::TotalFloors 
// Initialize the NumFloors variable in the Elevator class. 
//---------------------------------------------------------------------- 
void 
Elevator::TotalFloors(int numFloors) 
{ 
    this->NumFloors = numFloors; 

    PassCounter = new int[ numFloors + 1 ]; 
    for(int i = 1; i < numFloors; i++) 
    PassCounter[ i ] = 0; 
} 

//---------------------------------------------------------------------- 
// Elevator::ThreadPtr 
// Returns the Thread responsible for the Elevator functions 
//---------------------------------------------------------------------- 
Thread* 
Elevator::ThreadPtr() 
{ 
    return T; 
} 

//---------------------------------------------------------------------- 
// Elevator::IsAtCapacity 
// Determine if the Elevator has reached its maximum capacity. Returns 
// a boolean value. 
//---------------------------------------------------------------------- 
bool 
Elevator::IsAtCapacity() 
{ 
    if(this->OnBoard < MAX_CAPACITY) 
    return false; 

    return true; 
} 

//---------------------------------------------------------------------- 
// Elevator::PassengerDirection 
// Determines if the elevator and a passenger are heading in the 
// different directions. Returns a boolean value of true if they are 
// and a boolean value of false if headed in the same direction. 
// 
// "direction" is a boolean representing the passenger's direction 
//---------------------------------------------------------------------- 
bool 
Elevator::PassengerDirection(bool direction) 
{ 
    if(direction && this->Direction) 
    return false; 
    if(direction || this->Direction) 
    return true; 

    return false; 
} 

//---------------------------------------------------------------------- 
// Elevator::LoadPassenger 
// Load a new passenger onto the elevator. First check if the elevator 
// is at capacity and if the passenger is traveling in the same 
// direction. The elevator uses a semaphore before loading the 
// passenger onto its queue, and releases the semaphore when it's done. 
// 
// "p" is a pointer to a passenger struct to extract passenger info 
//---------------------------------------------------------------------- 
void 
Elevator::LoadPassenger(Passenger *P) 
{ 
    if(this->IsAtCapacity() || this->PassengerDirection(P->pDirection())) 
    return ; 

    lock->Acquire(); 
    AddToQueue(P); 


    while(CurrentFloor != P->ToFloor()) 
    PassengerCV->Wait(lock); 
    lock->Release(); 
} 

//---------------------------------------------------------------------- 
// Elevator::UnloadPassenger 
// Unload a passenger from the elevator. First check if the passenger's 
// destination is the same as the current floor. If so, unload the 
// passenger and decrement the number of passengers on board. If not, 
// then re-add the passenger to the queue at the front of the list. 
// 
// "p" is a pointer to a passenger struct to extract passenger info 
//---------------------------------------------------------------------- 
void 
Elevator::UnloadPassenger(Passenger *P) 
{ 
    lock->Acquire(); 


    if(PassCounter[ CurrentFloor ] == 0) 
    ElevatorCV->Signal(lock); 

    lock->Release(); 
} 

//---------------------------------------------------------------------- 
// Elevator::PassengersWaiting 
// Determine if a passenger is queued waiting to go up or waiting to go 
// down. Depending on the elevator's direction, a passenger will be 
// picked up, if one is queued. If one is not queued, then the function 
// terminates. 
//---------------------------------------------------------------------- 
void 
Elevator::PassengersWaiting() 
{ 
    Passenger *p = 0; 
    S->P(); 
    if(Direction) 
    if(!UpQueue->IsEmpty()) 
    { 
     p = ((Passenger *)UpQueue->Remove()); 
    } 
    if(!Direction) 
    if(!DownQueue->IsEmpty()) 
    { 
     p = ((Passenger *)DownQueue->Remove()); 
    } 

    if(p != 0 && !PassengerDirection(p->pDirection())) 
    LoadPassenger(p); 
    S->V(); 
} 

//---------------------------------------------------------------------- 
// Elevator::Run 
// Starts the elevator at floor 1 and runs until floor "NumFloors." The 
// elevator prints the floor number when it arrives. It then loops 
// through the first passenger in the queue, removing that passenger, 
// while the passenger's destination is the current floor. Once all 
// passengers are unloaded, we check if the elevator can accommodate 
// any additonal passengers. 
//---------------------------------------------------------------------- 
void 
Elevator::Run() 
{ 
    ASSERT(OnBoard >= 0); 
    while(1) 
    { 
    lock->Acquire(); 
    if(Queue->IsEmpty()) 
     ElevatorCV->Wait(lock); 
    lock->Release(); 

    while(!(Queue->IsEmpty())) 
    { 
     for(int i = 0; i < 50; i++) 
     currentThread->Yield(); 

     Person *p = Queue->Remove(); 
     Direction = p->pDirection(); 

     lock->Acquire(); 
     if(Direction) 
     CurrentFloor--; 
     else if(!Direction) 
     CurrentFloor++; 

     printf("Elevator arrives on floor %d\n", CurrentFloor); 
     if(CurrentFloor == p->ToFloor) 
     { 
     delete p; 
     PassengerCV->Broadcast(lock); 
     ElevatorCV->Wait(lock); 
     } 

     lock->Release(); 
    } 

    lock->Acquire(); 
    List *TempList = Queue; 
    if(Direction) 
    { 
     Queue = DownQueue; 
     DownQueue = TempList; 
    } 
    else if(!Direction) 
    { 
     Queue = UpQueue; 
     UpQueue = TempList; 
    } 
    lock->Release(); 
    } 
} 

//---------------------------------------------------------------------- 
// Elevator::AddToQueue 
// Adds a passenger to the UpQueue or the DownQueue from the ThreadTest 
// file. 
//---------------------------------------------------------------------- 
void 
Elevator::AddToQueue(Passenger *P) 
{ 
    if(Queue->IsEmpty()) 
    { 
    Queue->SortedInsert((void*) P, P->ToFloor()); 
    OnBoard++; 
    } 
    else 
    { 
    Person *P2 = (Person*) Queue->Remove(); 
    if(P->FromFloor() != CurrentFloor) 
    { 
     if(P2->FromFloor() < P->FromFloor()) 
     UpQueue->Append((void*) P); 
     else 
     { 
     Queue->SortedInsert((void*) P, P->ToFloor()); 
     OnBoard++; 
     } 
    } 
    else 
    { 
     if(P->FromFloor() < CurrentFloor) 
     DownQueue->Append((void*) P); 
     else 
     { 
     Queue->SortedInsert((void*) P, P->ToFloor()); 
     OnBoard++; 
     } 
    } 

    Queue->SortedInsert((void*) P2, P2->ToFloor()); 
    OnBoard++; 
    } 
} 

void 
Elevator::Request(Person *P) 
{ 
    lock->Acquire(); 
    bool Enter = true; 

    if(!(Queue->IsEmpty())) 
    if(P->FromFloor != CurrentFloor) 
     Enter = false; 

    if(CurrentFloor == P->FromFloor() && Enter) 
    { 
    lock->Release(); 
    return; 
    } 

    AddToQueue(P); 
    while(CurrentFloor != P->FromFloor) 
    PassengerCV->Wait(lock); 

    lock->Release(); 
} 

//---------------------------------------------------------------------- 
// Passenger::Passenger 
// Initialize a passenger and load the Passenger class' private 
// variables. 
// 
// "atFloor" is an int representing a passenger's origin floor 
// "goToFloor" is an int representing a passenger's destination floor 
// "PID" is an int representing a passenger's ID 
//---------------------------------------------------------------------- 
Passenger::Passenger(int fromFloor, int toFloor, int tID): toFloor(toFloor), fromFloor(fromFloor), tID(tID) 
{ 
    passengerDirection = fromFloor < toFloor; 
} 

//---------------------------------------------------------------------- 
// Passenger::ToFloor 
// Returns the value of the "toFloor" variable, which represents the 
// floor a Passenger wants to reach. 
//---------------------------------------------------------------------- 
int 
Passenger::ToFloor() 
{ 
    return this->toFloor; 
} 

//---------------------------------------------------------------------- 
// Passenger::FromFloor 
// Returns the value of the "fromFloor" variable, which represents the 
// floor a Passenger started on. 
//---------------------------------------------------------------------- 
int 
Passenger::FromFloor() 
{ 
    return this->fromFloor; 
} 

//---------------------------------------------------------------------- 
// Passenger::ID 
// Returns the value of the "tID" variable, which represents a 
// Passenger's unique identification number. Also, a thread ID for each 
// Passenger/Thread. 
//---------------------------------------------------------------------- 
int 
Passenger::ID() 
{ 
    return this->tID; 
} 

//---------------------------------------------------------------------- 
// Passenger::pDirection 
// Returns the value of the "passengerDirection" variable, which 
// represents which direction a Passenger is headed. A true value means 
// that a Passenger is headed up and a false value means that a 
// Passenger is headed down. 
//---------------------------------------------------------------------- 
bool 
Passenger::pDirection() 
{ 
    return passengerDirection; 
} 

任何形式的幫助或想法會非常感謝!我已經投資了Bjarne Stroustrup的C++編程語言,希望它能在將來幫助解決這些問題。

回答

1

我記得你必須在/ code中編輯Makefile.common,然後從那裏運行一個gmake,它會在NACHOS中自動生成目錄級makefile。

如果你打開Makefile.common,你會看到這幾行:

THREAD_H =../threads/copyright.h\ 
../threads/list.h\ 
../threads/scheduler.h\ 
../threads/synch.h \ 
../threads/synchlist.h\ 
../threads/system.h\ 
../threads/thread.h\ 
../threads/utility.h\ 
../machine/interrupt.h\ 
../machine/sysdep.h\ 
../machine/stats.h\ 
../machine/timer.h 

THREAD_C =../threads/main.cc\ 
../threads/list.cc\ 
../threads/scheduler.cc\ 
../threads/synch.cc \ 
../threads/synchlist.cc\ 
../threads/system.cc\ 
../threads/thread.cc\ 
../threads/utility.cc\ 
../threads/threadtest.cc\ 
../machine/interrupt.cc\ 
../machine/sysdep.cc\ 
../machine/stats.cc\ 
../machine/timer.cc 

THREAD_S = ../threads/switch.s 

THREAD_O =main.o list.o scheduler.o synch.o synchlist.o system.o thread.o \ 
utility.o threadtest.o interrupt.o stats.o sysdep.o timer.o 

你要添加命令行來這裏編譯電梯類。你做到了嗎?

+0

是的,這絕對是正確的!我想我沒有注意到Makefile包含了一個來自更高目錄的Makefile.common和Makefile.dep。一旦我在Makefile.common中添加了必要的行,一切都奏效了! – wmarquez

+0

真棒,很高興幫助! –

0

你的執行在哪裏ToFloor()等?您的Elevator.h將電梯類的接口定義爲。您需要鏈接實現(即在Makefile的鏈接行中有Elevator.o)。

所以,在你Makefile你將有:

threadtest: threadtest.o Elevator.o 
    $(CXX) $(CXXFLAGS) -o [email protected] threadtest.o Elevator.o 
+0

對不起,我完全忘了添加實現。我在Elevator.cc中有這些,但是如何將實現鏈接到代碼的其餘部分?我認爲這是我最大的困惑。 – wmarquez

0

是否所有的功能是問題(請求,FromFloor,ToFloor等)有一個機構?你是否定義了hte函數?如果您已經定義了它們,您是否編譯了定義並將它們連接在一起?

+0

是的,我在Elevator.cc類中定義了這些函數,但我不是100%確定如何編譯這些定義並將它們鏈接在一起。 – wmarquez

0

Passanger的方法未實現。你需要爲他們提供

Passenger(int fromFloor, int toFloor, int tID); 
int FromFloor(); 
int ToFloor(); 
int ID(); 
bool pDirection() 

我也反對把更多的類定義在同一個文件的編碼風格,但是這是一個編碼風格偏好。

包含電梯頭後的前向聲明也不是必需的。

class Passenger; class電梯;

+0

我將這些Passenger類方法的實現放在Elevator.cc文件的底部(即在所有Elevator類方法之後)。我注意到使用這種風格的NACHOS代碼本身的一些例子,我只是選擇複製它。老實說,我認爲減少課程可以避免可能發生的任何鏈接問題。但我想我仍然在同一條船上。 – wmarquez

+0

爲了避免make文件語法的擔心,爲什麼不快速在Eclipse CDT或其他IDE中使用代碼確保它可以工作,然後編寫make文件或automake autoconf文件。 – Dragos

相關問題