I'm a C++ noob, currently trying to create a text based maze game for degree. I'm replacing my currently working array with a vector (for dynamic size) and reading a txt file of chars to it.
PROBLEM: I get no IDE errors, but get RUNTIME ERROR: Debug assertion Failed! Expression: vector subscript out of range!
I have used the pushback instead of RoomFile=data, but I don't know if I need to amend other functions. I read the array/vector data in another class (player) and will include code if needed.
I have searched everywhere to find solution, but can't find any projects using a similar vector structure. What am I doing wrong?
Room.h
#pragma once
#include <vector>
#include "stdafx.h"
class Room
{
private: //data acceessed by functions
char floor;
char wall;
char door;
int width;
int height;
public:
std::vector <char> data;
Room* North;
Room* East;
Room* South;
Room* West;
Room* Next;
int NorthX;
int NorthY;
int EastX;
int EastY;
int SouthX;
int SouthY;
int WestX;
int WestY;
char RoomFile;
//where functions go
Room(void);
void CreateRoom(int RoomNo);
void PrintRoom();
~Room(void);
};
Room.cpp
#include <iostream>
#include <fstream>
#include <streambuf>
#include <string>
#include <sstream>
#include "Room.h"
#include <cstdlib>
using namespace std;
Room::Room(void)
{
floor = ' ';
wall = '#';
door = 'X';
width = 11;
height = 11;
North=0;
East=0;
South=0;
West=0;
Next=0;
NorthX=0;
NorthY=0;
EastX = 0;
EastY = 0;
SouthX= 0;
SouthY=0;
WestX=0;
WestY=0;
}
void Room::CreateRoom(int RoomNo)
{
std::vector<char> data;
char RoomFile[255];
ifstream infile;
stringstream ss;
//LOAD CURRENT ROOM FROM FILE
ss << RoomNo;
string str = ss.str();
string fname = ("Room");
fname.append(str);
fname.append(".txt");
infile.open(fname);
infile.clear();
infile.seekg(0);
if(infile.is_open())
{
while(!infile.eof()) // To get you all the lines.
{
int i;
for(int row = 0; row < width; row++)
{
infile.getline(RoomFile, 256);
i = 0;
for(int col = 0; col < height; col++)
{
data.push_back(RoomFile[i]);
i++;
}
}
}
}
else
{
cout << "ERROR: infile not open" << endl;
}
infile.close();
//ASSIGN DOORS TO ROOMS IF NEEDED
// treating the array as being one-dimensional.
for(int row = 0; row < width; row++)
{
for(int col = 0; col < height; col++)
{
if(North!=NULL)
{
if(data[row*height+col]=='X')
{
//data[row*height+col] = door;
NorthX=row;
NorthY=col;
}
}
if(East!=NULL)
{
if(data[row*height+col]=='X')
{
//data[row*height+col] = door;
EastX = row;
EastY = col;
}
}
if(South!=NULL)
{
if(data[row*height+col]=='X')
{
//data[row*height+col] = door;
SouthX = row;
SouthY = col;
}
}
if(West!=NULL)
{
if(data[row*height+col]=='X')
{
//data[row*height+col] = door;
WestX = row;
WestY = col;
}
}
}
}
}
void Room::PrintRoom()
{
for(int row = 0; row < width; row++)
{
for(int col = 0; col < height; col++)
{
cout << data[col * height + row];
}
cout << "\n";
}
cout << endl;
}
Room::~Room(void)
{
}
Stack Trace
// throw -- terminate on thrown exception REPLACEABLE
#define _HAS_EXCEPTIONS 0
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <crtdbg.h>
_STD_BEGIN
#ifdef _DEBUG
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const wchar_t *message, const wchar_t *file, unsigned int line)
{ // report error and die
if(::_CrtDbgReportW(_CRT_ASSERT, file, line, NULL, L"%s", message)==1)
{
::_CrtDbgBreak();
}
}
_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Debug_message(const unsigned short *message, const unsigned short *file, unsigned int line)
{ // report error and die
_Debug_message((wchar_t *) message, (wchar_t *) file, line);
}
#endif
_STD_END
I think this is the error: What you are printing in PrintRoom is the std::vector<char> data inside your class, while the std::vector<char> data that you filled up is local to CreateRoom method.
void Room::CreateRoom(int RoomNo)
{
std::vector<char> data; // remove this to solve
In your PrintRoom() function, you have
cout << data[col * height + row];
instead of
cout << data[row * height + col];
Related
I working on a program that simulates travel agents booking flights in parallel. It spins up a process for each agent and works against an array of Plane objects held in shared memory.
I'm getting a segmentation fault when I try to push a row of seats back to the plane. The method to parse the input file calls a SetSeats() method on Plane objects. Each Plane contains a vector<map<char, Seat>> (each index of the vector is a row, each key of each map is the letter of a seat on that row). When I call SetSeats() it goes fine through adding seats to the first map, i.e. the first row of seats. It throws the segfault when I try to push the map back to the seats vector.
I saw something online about pushing back custom classes to vectors needing deconstructors, so I added them to Seat.h and Plane.h.
Code for the main program:
#include <iostream>
#include <map>
#include <vector>
#incluce <string>
#include <fstream>
#include "Seat.h"
#include "Plane.h"
void ParseInputFile(ifstream &inFS, int numPlanes, int &numAgents);
int shmid;
int *timer;
int numPlanes, numAgents;
struct sembuf *ops;
Plane *sharedPlanes;
map<string, Plane*> planes;
using namespace std;
int main(int argc, char *argv[])
{
ifstream inFS;
// code to get an input file from command line arguments and get number of planes from it
// set up shared memory segment
long key = XXX; // just a long integer
int nbytes = 1024;
shmid = shmget((key_t)key, nbytes, 0666 | IPC_CREAT);
if (shmid == -1)
{
printf("Error in shared memory region setup.\n");
perror("REASON");
exit(2);
}
// initialize global variables
sharedPlanes = new Plane[numPlanes];
timer = new int;
ops = new sembuf[1];
// attached shared pointers to shared memory segment
sharedPlanes = (Plane*)shmat(shmid, (Plane*)0, 0);
timer = (int*)shmat(shmid, (int*)0, 0);
*timer = 0;
inFS.open(inputFile);
ParseInputFile(inFS, numPlanes, numAgents); // breaks in here
// the rest of main()
}
void ParseInputFile(ifstream &inFS, int numPlanes, int &numAgents)
{
string line = "";
bool foundNumberOfPlanes = false;
bool foundPlanes = false;
bool foundNumberOfAgents = false;
bool lookingForAgent = false;
bool foundAgent = false;
int planeNo = 0;
int agentNo = 0;
int opNo = 0;
map<string, Operation> ops;
vector<Request> agentRequests;
while (getline(inFS, line))
{
if (!CommonMethods::IsWhitespace(line))
{
// code to read first line
if (foundNumberOfPlanes && !foundPlanes)
{
// parse a line from the input file to get details about the plane
Plane *plane = &sharedPlanes[planeNo];
unsigned int rows = xxx; // set based on the plane details
unsigned int seatsPerRow = xxx; set based on the plane details
plane->SetSeats(rows, seatsPerRow); // this is the method where I get the seg fault
// finish defining the plane
continue;
// the rest of the method
}
}
}
}
Code for Plane.h:
#pragma once
#include <iostream>
#include <string>
#include <map>
#include <tuple>
#include <vector>
#include "Seat.h"
#include "Exceptions.h"
#include "ReservationStatus.h"
#include "CommonMethods.h"
using namespace std;
class Plane
{
private:
vector<map<char, Seat>> seats;
unsigned int numberOfRows, numberOfSeatsPerRow;
public:
Plane(unsigned int numberOfRows, unsigned int numberOfSeatsPerRow);
Plane() {}
void SetSeats(unsigned int numberOfRows, unsigned int numberOfSeatsPerRow);
};
void Plane::SetSeats(unsigned int numberOfRows, unsigned int numberOfSeatsPerRow)
{
//cout << "Clearing old seats" << endl;
if (!seats.empty())
{
//cout << "Seats not empty" << endl;
for (int i = 0; i < (int)seats.size(); i++)
{
//cout << "checking row " << i << endl;
if (!seats.at(i).empty())
{
//cout << "Row " << i << " not empty" << endl;
seats.at(i).clear();
}
}
}
cout << "Rows: " << numberOfRows << ", Seats: " << numberOfSeatsPerRow << endl;
this->numberOfRows = numberOfRows;
this->numberOfSeatsPerRow = numberOfSeatsPerRow;
for (unsigned int i = 0; i < this->numberOfRows; i++)
{
map<char, Seat> row;
for (unsigned int j = 0; j < this->numberOfSeatsPerRow; j++)
{
Seat seat;
seat.RowNumber = i + 1;
seat.SeatLetter = j + 'A';
//cout << "Inserting seat " << seat.RowNumber << seat.SeatLetter << endl;
row.insert(pair<char, Seat>(seat.SeatLetter, seat));
}
if (!row.empty())
{
cout << "inserting row " << (i + 1) << endl;
seats.push_back(row);
}
}
}
void Plane::ProcessWaitAny(int t)
{
while (!WaitingList.empty())
{
bool booked = false;
string pass = WaitingList.front();
WaitingList.pop();
for (unsigned int j = 0; j < numberOfRows; j++)
{
if (booked)
break;
for (unsigned int k = 0; k < numberOfSeatsPerRow; k++)
{
Seat *s = &seats.at(j)[k + 'A'];
if (!s->IsBooked)
{
Reserve(s, pass);
booked = true;
string seatNo = to_string(j);
seatNo += (k + 'A');
cout << "Passenger " << pass << " booked into seat " << seatNo << " at time " << t << endl;
break;
}
}
}
if (!booked)
return;
}
}
Code for Seat.h
#pragma once
#include <iostream>
#include <string>
#include <queue>
using namespace std;
struct Seat
{
string Passenger = "";
bool IsBooked = false;
unsigned int RowNumber;
char SeatLetter;
queue<string> WaitingList;
};
I have made a minimum working example of your problem:
#include <vector>
#include <sys/shm.h>
#include <iostream>
class Bar {
public:
Bar() {};
std::vector<int> vec;
};
int main() {
int shmid;
Bar* a = new Bar();
a->vec.push_back(1);
// set up shared memory segment
long key = 0x123455; // just a long integer
int nbytes = 1024;
shmid = shmget((key_t)key, nbytes, 0666 | IPC_CREAT);
if (shmid == -1)
{
printf("Error in shared memory region setup.\n");
perror("REASON");
exit(2);
}
a = (Bar*)shmat(shmid, (Bar*)0, 0);
a->vec.push_back(2);
}
The problem is your wrong usage of shmat. The pointer sharedPlane just points to some unitialized shared memory. You have to make sure that the address provided by key is 'right'. To do this, do the following:
Your other process, call Plane * other_process_sharedPlane = new Plane();. Remove the line sharedPlanes = new Plane[numPlanes]; from your main programm.
In your main process, set key to the value of other_process_sharedPlane
Then you can call shmget and shmadd
I am making a console game and I am making a map. The map is an array of vectors. The vectors contain the characters that I am printing to the console. My code:
"Window.h"
#include <string>
#include <vector>
class Row {
public:
std::vector<char> row;
int id;
Row();
void printRow();
void resetRow();
void insertStringIntoRow(std::string toInsert, int startIndex);
std::vector<char> getRow() {
return row;
}
};
class Window {
public:
void showMap();
void writeToMap(std::string stringToInsert, int rowNum, int startIndex);
void writeInRectangle(std::string stringToWrite, int rowNum, int startIndex);
void setCursorToPosition(int x, int y);
void resetMap();
Row getRowAt(int index);
};
void initColors();
void setWindow(Window windowToSet);
Window getGameWindow();
"Window.cpp"
#include "Window.h"
#include <iostream>
#include <Windows.h>
#include <WinBase.h>
#include <stdlib.h>
#include "color.h"
using namespace eku;
Row map[25];
//Class Window
void Window::showMap() {
setCursorToPosition(0, 0);
for (int i = 0; i < 25; i++) {
getRowAt(i).printRow();
}
}
void Window::writeToMap(std::string stringToInsert, int rowNum, int startIndex) {
Row r = getRowAt(rowNum);
r.insertStringIntoRow(stringToInsert, startIndex);
}
void Window::writeInRectangle(std::string stringToWrite, int rowNum, int startIndex) {
if (startIndex != 0) {
std::string topbar = "~";
for (int i = 0; i < stringToWrite.length() + 2; i++) {
topbar += ' ';
}
topbar += '^';
getRowAt(rowNum - 1).insertStringIntoRow(topbar, startIndex - 1);
}
std::string toInsert = "~ ^" + stringToWrite + "~ ^";
getRowAt(rowNum).insertStringIntoRow(toInsert, startIndex - 1);
if (startIndex != 25) {
std::string bottombar = "~";
for (int i = 0; i < stringToWrite.length() + 2; i++) {
bottombar += ' ';
}
bottombar += '^';
getRowAt(rowNum + 1).insertStringIntoRow(bottombar, startIndex - 1);
}
}
void Window::setCursorToPosition(int x, int y) {
HANDLE hOut;
COORD Position;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
Position.X = x;
Position.Y = y;
SetConsoleCursorPosition(hOut, Position);
}
void Window::resetMap() {
for (Row row : map) {
row.resetRow();
}
}
Row Window::getRowAt(int index)
{
return map[index];
}
//Class Row
const char WHITEBACKCOL = '~';
const char DEFCOL = '^';
int i = 0;
Row::Row() {
row.resize(80, ' ');
id = i;
i++;
}
void Row::printRow() {
for (int i = 0; i < row.size(); i++) {
switch (row[i]) {
case WHITEBACKCOL:
setcolor(black, white);
case DEFCOL:
setcolor(white, black);
default:
std::cout << row[i];
}
}
}
void Row::resetRow() {
row.resize(80);
for (int i = 0; i < 80; i++) {
row[i] = ' ';
}
}
void Row::insertStringIntoRow(std::string toInsert, int startIndex) {
int endIndex = (startIndex + toInsert.length());
int stringPos = 0;
for (int i = startIndex; i < endIndex; i++) {
if (i < row.size() - 1) {
row.at(i) = toInsert[stringPos];
}
else {
row.push_back(toInsert[stringPos]);
}
stringPos++;
}
}
Window defWindow;
void initColors() {
concolinit();
setcolor(white, black);
}
void setWindow(Window windowToSet) {
defWindow = windowToSet;
}
Window getGameWindow() {
return defWindow;
}
"Main.cpp"
#include <iostream>
#include "GameEngine.h"
#include "Window.h"
int main() {
setWindow(Window());
initColors();
getGameWindow().writeInRectangle("Hia", 1, 10);
getGameWindow().showMap();
}
Whenever I call showMap() all I get is a blank console. It seems to be only printing the default map of spaces instead of the text I entered. I also tried using just printRow() to print the single rows that I edited but they also showed only spaces.
I was able view changes to the vector row in the insertStringIntoRow() method but then even though the changes should there they didn't show anywhere else. It almost seems like my Row object is being created every time I access it. I am new to C++ so any help is appreciated. Thanks in advance!
This function is being used incorrectly. Warning signs are that it is non-const, yet used as an accessor. It returns a copy of the vector, not a reference.
std::vector<char> getRow() {
return row;
}
The way you are calling it, you are expecting it to modify the row in-place, but all you are actually doing is modifying a copy of the row, which is then immediately discarded. Example:
getRowAt(rowNum).insertStringIntoRow(toInsert, startIndex - 1);
The easy fix for this behaviour is to return a reference from getRow():
std::vector<char> & getRow() {
return row;
}
The other thing to fix is where you assign this to a temporary variable instead of using it inline. In that case, you can make the temporary a reference:
Row & r = getRowAt(rowNum);
r.insertStringIntoRow(stringToInsert, startIndex);
The correct way to implement this is to also provide a const-version, so that it can still be called on const objects where the caller does not want to modify it.
const std::vector<char> & getRow() const {
return row;
}
I've only put this extra bit in here because it is good practice, but you should not do it in this program. It would clutter your code which is already full of worse practices =)
send a reference to writeToMap() instead of variables.
This will make changes to the original map objects instead of copy-variables.
I'm doing a project with C++ and my program keeps crashing down when I try to run it...here is my code (two files: main.cpp and PlacementHead.cpp):
main.cpp:
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <ctime>
#include <climits>
#include "PlacementHead.h"
// Main
int main (int argc, char * const argv[]) {
PlacementHead h1(4,2,1,"aabcbbca");
PlacementHead h2(4,2,1,"babcabca");
return 0;
}
PlacementHead.cpp:
#include "PlacementHead.h"
#include <string>
#include <iostream>
#include <string.h>
PlacementHead::PlacementHead(int width, int height, int gap, char* s) {
width_ = width;
height_ = height;
gap_ = gap;
size_ = width*height;
set_ = new char[size_];
from_ = new int[size_];
original_ = new char[size_];
strcpy(set_,s);
strcpy(original_,s);
}
PlacementHead::~PlacementHead() {
}
int PlacementHead::getSize() { return size_; }
int PlacementHead::getHeight() { return height_; }
int PlacementHead::getWidth() { return width_; }
int PlacementHead::getGap() { return gap_; }
char PlacementHead::getNozzle(int i) {
return set_[i-1];
}
void PlacementHead::setNozzle(int i, char c) {
set_[i-1] = c;
}
void PlacementHead::markNozzle(int i, int bankPos) {
set_[i-1] = ' ';
from_[i-1] = bankPos;
}
int PlacementHead::getNextUnmarkedPos() {
for (int i=0; i<size_; i++) {
if (set_[i]!=' ') {
return i+1;
}
}
return 0;
}
int PlacementHead::getBankPos(int i) {
return from_[i-1];
}
void PlacementHead::reset() {
//for (int i=0; i<size_; i++) {
// set_[i] = original_[i];
//}
strcpy(set_,original_);
}
void PlacementHead::print() {
std::cout << "placementhead:\n";
for (int h=height_; h>0; h--) {
for (int w=width_; w>0; w--) {
int i = ((h-1)*width_)+w;
std::cout << getNozzle(i);
}
std::cout << "\n";
}
}
If I try to run the main.cpp, I get this:
Once I got this also (I don't get this every time, which bugs me...):
Now here is also one thing to take into account: If I comment out the second line where PlacementHead h2-object is created the code runs okay, but IF I create more than one PlacementHead-objects the program crashes again...
Any advices what might be causing this?
Thank you for any help!! =)
P.S.
My platform is Windows 7, Codeblocks 12.11 and GNU GCC Compiler
UPDATE:
In case you couldn't see the text on the second picture here it is:
You should
increase size_ by +1 too avoid buffer overrun (or, use std::vector instead, see below)
disable copy construction/assignment
add a proper destructor
use initialization lists
check the order in which members are declared (since this is also the order in which members are initialized!)
remove the default constructor, since it doesn't initialize a single member
include <cstring> instead of <string.h> on modern compilers (so you get namespaces C standard library functions)
Consider using std::vector instead of manual arrays. This will save you much trouble. Think of how you are going to get your constructor exception safe?
Here is a sample fixing all of the above:
#include <iostream>
#include <vector>
#include <cstring>
struct PlacementHead {
int width_, height_, gap_;
size_t size_;
std::vector<char> set_, original_;
std::vector<int> from_;
PlacementHead(int width, int height, int gap, const char* s) :
width_(width),
height_(height),
gap_(gap),
size_(width * height),
set_(s, s + std::min(strlen(s), size_)),
original_(set_),
from_(size_)
{
set_.resize(size_);
original_.resize(size_);
}
size_t getSize() { return size_; }
int getHeight() { return height_; }
int getWidth() { return width_; }
int getGap() { return gap_; }
char getNozzle(int i) { return set_[i - 1]; }
void setNozzle(int i, char c) { set_[i - 1] = c; }
void markNozzle(int i, int bankPos) {
set_[i - 1] = ' ';
from_[i - 1] = bankPos;
}
int getNextUnmarkedPos() {
for(unsigned i = 0; i < size_; i++) {
if(set_[i] != ' ') {
return i + 1;
}
}
return 0;
}
int getBankPos(int i) { return from_[i - 1]; }
void reset() {
//for (int i=0; i<size_; i++) {
// set_[i] = original_[i];
//}
set_ = original_;
}
void print() {
std::cout << "placementhead:\n";
for(int h = height_; h > 0; h--) {
for(int w = width_; w > 0; w--) {
int i = ((h - 1) * width_) + w;
std::cout << getNozzle(i);
}
std::cout << "\n";
}
}
};
// Main
int main (int argc, char * const argv[]) {
PlacementHead h1(4,2,1,"aabcbbca");
PlacementHead h2(4,2,1,"babcabca");
return 0;
}
size_ = width*height; should be size_ = (width*height)+1; so the string can be null terminated. Currently you are writing to unallocated memory causing undefined behaviour
You have an off-by-one error in your constructor, as the strings you try to copy are not 8 characters, they are 9. The reason is that all string literals are also containing an extra special string termination character.
If you're using strings in C++, use std::string, it will help you tremendously with problems like these.
So I'm trying to create a 2 dimensional array of pointers to a object of type Piece. The problem is when i try assign a pointer to a piece to the array i get a segmentation fault. I realized I needed to initialize to array to sometime before I can start allocating but I can't get it right.
Here is the header file of Map which contains a 2-d array of pointers.
#ifndef MAP_H
#define MAP_H
#include <iostream>
#include <vector>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <string>
#include <cstring>
#include "Player.h"
#include "Sprite.h"
#include "Piece.h"
#include "Messages.h"
#include "PieceType.h"
using namespace std;
class Map
{
private:
Piece*** pieces;
int startingX;
int startingY;
int width;
int height;
string mapName;
public:
Map(string);
~Map();
void printMap() const;
Piece* pieceType(char);
void setSprite(Piece*);
void firstMove();
void resetMap(string);
bool moveUp(int, int);
bool moveDown(int, int);
bool moveLeft(int, int);
bool moveRight(int, int);
int getHeight();
int getWidth();
};
#endif
The array I'm talking about is pieces.
I try to allocate this in the constructor of Map.
Map::Map(string name)
{
ifstream map;
string line;
string dimention;
mapName = name;
map.open(name.c_str());
if (map.good())
{
getline (map, line);
int i = 0;
while(line[i] != 'X')
{
dimention[i] = line[i];
i++;
}
stringstream convert(dimention);
convert >> width;
int temp = i;
dimention = "";
i = 1;
while(line[(i + temp)] != '\0')
{
dimention[i] = line[(i + temp)];
i++;
}
stringstream convertTwo(dimention);
convertTwo >> height;
for (int i = 0; i < height; i++)
{
if (!(map.eof()))
{
getline (map, line);
}
else
{
cout << "Error with file" << endl;
break;
}
for (int j = 0; j < width; j++)
{
pieces[i][j] = pieceType(line[j]); //This is where I'm getting the segmentation fault
cout << "assigned" << endl;
if ((pieces[i][j])->getType() == WAYPOINT)
{
if (pieces[i][j]->getWaypointType() == 0)
{
startingX = j;
startingY = i;
}
}
else
{
(pieces[i][j])->setXCordinate(j);
(pieces[i][j])->setYCordinate(i);
}
}
}
}
}
Where name is a string that holds the name of the file that has the information for loading a particular map.
Also the function pieceType is as follows:
Piece* Map::pieceType(char type)
{
Piece* temp;
if (type == '.')
{
return NULL;
}
if (type == 'S')
{
temp = new Waypoint(0);
return temp;
}
if (type == 'E')
{
temp = new Waypoint(1);
return temp;
}
}
Waypoint is a derived class of Piece.
The problem is indeed that you have to initialize that array. Like this:
pieces=new Piece**[height];
for(int i=0;i<height;i++){
pieces[i]=new Piece*[width];
}
Write that just after you get width and height, and before you start using pieces.
But something you should know: for each new, there should be a corresponding delete, or else that memory will never be freed, and you will get a memory leak. To free that memory, add this in your destructor:
for(int i=0;i<height;i++){
for (int j = 0; j < width; j++){
delete pieces[i][j];
}
delete[] pieces[i];
}
delete[] pieces;
This assumes that every pieces[i][j] contains either an object allocated with new or NULL, and it works with both. Looking at your code, that seems to be your case. However, it would not work if one of them is not assigned (not your case).
Use std::vector<std::vector<Pieces>>instead of (trying to, because it does not work) reinventing the wheel. Its safe, easy, and avoids getting manual-memory-management headaches.
It's crashing at the very end of the main() function where it needs to delete the starters objects. The error message that pops up when I run the program says: Debug assertion failed! Expression: _BLOCK_IS_VALID(pHead->nBlockUse). How do i fix it from crashing when deleting the starters objects?
#include <iostream>
#include <fstream>
#include "olympic.h"
using namespace std;
ofstream csis;
int main() {
const int lanes = 4;
Ranker rank(lanes);
csis.open("csis.txt");
// First make a list of names and lane assignments.
Competitor* starters[lanes];
starters[0] = new Competitor("EmmyLou Harris", 1);
starters[1] = new Competitor("Nanci Griffith", 2);
starters[2] = new Competitor("Bonnie Raitt", 3);
starters[3] = new Competitor("Joni Mitchell", 4);
// The race is run; now assign a time to each person.
starters[0]->setTime((float)12.0);
starters[1]->setTime((float)12.8);
starters[2]->setTime((float)11.0);
starters[3]->setTime((float)10.3);
// Put everyone into the ranker.
for (int i = 0; i < lanes; i++)
rank.addList(starters[i]);
// Now print out the list to make sure its right.
cout << "Competitors by lane are:" << endl;
csis << "Competitors by lane are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getLane(i)->print();
// Finally, show how they finished.
cout << "Rankings by finish are:" << endl;
csis << "Rankings by finish are:" << endl;
for (int i = 1; i <= lanes; i++)
rank.getFinish(i)->print();
for (int i = 0; i < lanes; i++)
delete starters[i];
csis.close();
}
ranker.cpp:
#include "ranker.h"
#include "competitor.h"
#include <stdlib.h>
Ranker::Ranker(int lanes) {
athlete = new Competitor*[lanes];
numAthletes = 0;
maxAthletes = lanes;
}
int Ranker::addList(Competitor* starter) {
if (numAthletes < maxAthletes && starter != NULL) {
athlete[numAthletes] = starter;
numAthletes++;
return numAthletes;
}
else
return 0;
}
Competitor* Ranker::getLane(int lane) {
for (int i = 0; i < numAthletes; i++) {
if (athlete[i]->getLane() == lane) {
return athlete[i];
}
}
return NULL;
}
Competitor* Ranker::getFinish(int position) {
switch(position) {
case 1:
return athlete[3];
break;
case 2:
return athlete[2];
break;
case 3:
return athlete[1];
break;
case 4:
return athlete[0];
break;
}
return NULL;
}
int Ranker::getFilled() {
return numAthletes;
}
Ranker::~Ranker() {
delete [] athlete;
}
competitor.h:
#ifndef _COMPETITOR_H
#define _COMPETITOR_H
class Competitor {
private:
char* name;
int lane;
double time;
public:
Competitor(char* inputName, int inputLane);
Competitor();
void setTime(double inputTime);
char* getName();
int Competitor::getLane();
double getTime();
void print();
~Competitor();
};
#endif
competitor.cpp:
#include "competitor.h"
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
Competitor::Competitor(char* inputName, int inputLane) {
name = inputName;
lane = inputLane;
}
Competitor::Competitor() {
name = 0;
lane = 0;
time = 0;
}
void Competitor::setTime(double inputTime) {
time = inputTime;
}
char* Competitor::getName() {
return name;
}
int Competitor::getLane() {
return lane;
}
double Competitor::getTime() {
return time;
}
void Competitor::print() {
cout << setw(20) << name << setw(20) << lane << setw(20) << setprecision(4) << time << endl;
}
Competitor::~Competitor() {
delete [] name;
}
Call stack:
before crash: http://i.imgur.com/d4sKbKV.png
after crash: http://i.imgur.com/C5cXth9.png
After you've added Competitor class, it seems the problem is that you delete its name in Competitor's destructor. But you assign it from string literal which can't really be deleted. I'm sure the stack trace leading to assertion will prove that.
One way of solving the problem would be using std::string to store the name.
Problem is when deleting the char* value on destructor, which is assigned with const char instead new char. So i have slightly changed the constructor to copy the const char to new char.
Competitor::Competitor(char* inputName, int charlen, int inputLane)
{
name = new char[charlen + 1];
memcpy(name , inputName, charlen );
name [charlen] = '\0';
lane = inputLane;
}