0

I am a novice C++ programmer who is currently a student at my local University. I am doing this assignment and I am told to create a linked list of a class called Car, to create a "train". I have two derived classes which simply print differently.

What I want to do is create a list of train cars, and have two pointers pointing to the current Car of each type (Chair and Sleeping) so I know when the user wants to book a seat of a certain type within the train, I know exactly where to go there. When a train is full, the plan is to put a new train of that type onto the end. Ex: Sleeping Car's last seat is booked, a new Sleeping Car will be created and put at the end of the train. The current Sleeping Car pointer will now point to the new Car at the end of the list.

Below is the code I have for this program:

carTest.cc

int main()
{
  int trainId = 0;
  list<Car> train;

  // Testing Car Default Constructor

  // Testing Car Constructor
  //cout << "Creating a \"Chair car\"... " << endl;

  Car* currChair = nullptr, *currSleep = nullptr;
  train.push_back(ChairCar(trainId++));
  currChair = &train.back();
  train.push_back(SleepingCar(trainId++));
  currSleep = &train.back();

  // Testing bookSeat
  //book_seat_test(car2);

  // Testing isTypeAvailable
  //is_type_available_test(car2);

  // Testing isBooked
  //is_booked_test(car2);

  // Testing mostAvailable
  //most_available_test(car2);

  // Testing firstAvailable
  //first_available_test(car2);

  // Testing getSeatNumber
  ///get_seat_number_test(car2);

  // Testing getIndex
  //get_index_test(car2);

  // Testing bookFirstAvailable
  //book_first_available_test(car2);

  // Testing chairCar printStatus
  //chairCar_print_status_test(*currChair);

  // Testing sleepingCar printStatus
  //sleepingCar_print_status_test(*currSleep);

  currSleep = nullptr;
  currChair = nullptr;
  return 0;
}

car.h

class Car
{
 public:
  class Seat
  {
  public:
  Seat() : row(-1), col(-1) {};
  Seat(int i, int j) : row(i), col(j) {};
    int getRow() const { return row; }
    int getCol() const { return col; }

  private:
    int row;
    int col;
  };

 public:
  // Contructors
  Car();
  explicit Car(int, int, int);

  // Deconstructor
  ~Car();

  // Public Member Functions
  bool bookSeat(int, int);
  bool isTypeAvailable(int) const;
  bool isBooked() const;
  int mostAvailable() const;
  int firstAvailable(int) const;
  int getSeatNumber(int, int) const;
  Seat getIndex(int) const;
  int bookFirstAvailable(int);
  virtual void printStatus(int) const;

 protected:
  int carNumber;
  int rows;
  int cols;
  bool** plan;
  int* numAvailable;
  int columnSeperation;

  void init();
};

car.cc

// function: Deconstructor
// Description:
//             The Deconstructor simply frees the dynamically allocated memory
// of the data members plan and numAvailable if the members are not pointing 
// to the nullptr.
Car::~Car()
{
  if(plan != nullptr)
    {
      delete[] plan;
      plan = nullptr;
    }

  if(numAvailable != nullptr)
    {
      delete[] numAvailable;
      numAvailable = nullptr;
    }
}

chairCar.h

class ChairCar : public Car
{
 public:
  ChairCar();
  ChairCar(int);
 protected:
  void printStatus(int seatNumber) const;
};

#endif

chairCar.cc

#include "chairCar.h"

ChairCar::ChairCar() 
  : Car() 
{

}

ChairCar::ChairCar(int id) 
  : Car(7,4,id)
{

}

void ChairCar::printStatus(int seatNumber) const
{
  int maxSeatNumber = (rows-1)*cols+(cols-1);
  if(seatNumber > maxSeatNumber || seatNumber < 0)
    throw OutOfRangeException(seatNumber, 0, maxSeatNumber);

  cout << setw(columnSeperation) << ' ' << " |";
  cout << setw(columnSeperation) << "WL";
  cout << setw(columnSeperation) << "ML";
  cout << setw(columnSeperation) << "MR";
  cout << setw(columnSeperation) << "WR";
  cout << endl;
  Car::printStatus(seatNumber);
}

Sleeping Car is exactly the same as Chair Car except different arguments are sent to the base constructor. When I run the code in the main function I get

Error in `/home/w1llbedone/Programming/cpsc2620/assn/a4/carTest': double free or corruption (fasttop): 0x0000000000608010

I was trying to set the currCar pointers to nullptr before end of the scope to avoid this error. Can anyone explain to me why this happens? This is my first time posting on StackExchange, so I apologise for any lack of information. Any help would be greatly appreciated.

e0k
  • 6,961
  • 2
  • 23
  • 30
  • You shouldn't push a SleepCar into, 'list train;'. My guess it's been sliced into a Car via copy ctor. The scope of SleepCar is in the push_back, so its resource get freed. And at end of main, dtor of the copy (Car) in the list is also freed, therefore the double free. Use 'list train' or better yet 'list>'. Also ~Car() should be virtual. – Tony J Dec 03 '16 at 00:56
  • `Car` has a non-trivial destructor, but I don't see any copy constructor or copy assignment operator, so you're double-deleting your dynamically allocated members. – Miles Budnek Dec 03 '16 at 01:15
  • 4
    Possible duplicate of [What is The Rule of Three?](http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – Miles Budnek Dec 03 '16 at 01:15

1 Answers1

1

There are at least two major bugs in the partially-shown code. Bug #1:

list<Car> train;

// ...

train.push_back(ChairCar(trainId++));

This is a textbook case of object slicing. train is a std::list of Cars. The shown code constructs a ChairCar, and puts it into the list. This slices off the ChairCar subclass, and copies the Car superclass into the list.

Bug #2 is the primary bug here: this class violates the Rule Of Three. Car's destructor clearly deletes class members that were constructed in dynamic scope. However, the Car class does not define a copy constructor, nor the assignment operator. This pretty much rules out the chances of this class being Rule-of-three compliant.

So, these two bugs result in a lethal combination. During the initial object slicing, the temporary ChairCar object gets destroyed, but only after a copy of its Car superclass gets copy-constructed into the list's container. As part of the destruction, Car's destructor deletes its dynamically-scoped class members.

Then, eventually, the list container gets destroyed, and the sliced off Car, in the list, gets destroyed, and its destructor attempts to delete the pointers that were already deleted.

Game over.

Community
  • 1
  • 1
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148