0

我很难将我编写的 C++ 类与单独的“测试”C++ 文件链接起来。所以我有这三个文件:threadtest.cc、Elevator.h 和 Elevator.cc。在 Elevator.h 中,我定义了两个类:Passenger 和 Elevator。当我尝试从 threadtest.cc 文件中引用乘客或电梯的函数时,我得到未定义的引用错误。我做的第一件事是确保我在 threadtest.cc 中有一个 include 指令。我一直在尝试到处搜索链接和 NACHOS 类的一般指南,但我唯一能找到的是 NACHOS pdf 的路线图,它缺少出现此类问题时所需的详细信息。

我包含了 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 Makefile 用于编译 NACHOS 中的所有内容。以下是对应于 threadtest.cc 和 Elevator.h 的 Makefile 部分

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++ 编程语言,希望它能在未来帮助解决这些问题。

4

4 回答 4

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

您需要在此处添加行来编译您的 Elevator 类。你这样做了吗?

于 2013-10-05T17:51:34.133 回答
0

您在哪里实施诸如此类的方法ToFloor()?您Elevator.h定义了Elevator 类的接口。您需要在实现中进行链接(即Elevator.o在您的链接行中具有Makefile)。

因此,在您的身上,Makefile您将拥有:

threadtest:    threadtest.o Elevator.o
    $(CXX) $(CXXFLAGS) -o $@ threadtest.o Elevator.o
于 2013-10-04T20:41:18.587 回答
0

问题的所有功能(请求、FromFloor、ToFloor 等)是否都有主体?你定义了hte函数吗?如果您已经定义了它们,您是否正在编译定义并将它们链接在一起?

于 2013-10-04T20:42:50.130 回答
0

Passanger 的方法没有实现。你需要提供他们

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

我也反对将更多类定义放在同一个文件中的编码风格,但这是一种编码风格偏好。

也不需要包含电梯标题之后的前向声明。

类乘客;类电梯;

于 2013-10-04T20:45:37.183 回答