我很難將我編寫的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++編程語言,希望它能在將來幫助解決這些問題。
是的,這絕對是正確的!我想我沒有注意到Makefile包含了一個來自更高目錄的Makefile.common和Makefile.dep。一旦我在Makefile.common中添加了必要的行,一切都奏效了! – wmarquez
真棒,很高興幫助! –