According to the topic Pushing local objects into a list
The full class:
class Invader
{
public:
Invader(const Invader&other);
Invader();
~Invader();
public:
void Init(InvaderTypes invadertype, CIw2DImage *AlienImage);
void Update(float dt);
void Render();
void SetAlienImage(CIw2DImage *image){ mImageAlien = image; }
void setVisible(bool show) { Visible = show; }
bool isVisible() const { return Visible; }
Iw2DSceneGraph::CSprite *AlienSprite;
Iw2DSceneGraph::CAtlas *AlienAtals;
CIw2DImage *mImageAlien;
std::list<Bullet*> *Bullets;
CIwFMat2D Transform; // Transform matrix
bool Visible; // Sprites visible state
bool Canfire;
};
Invader::Invader()
{
}
Invader::Invader(const Invader&other)
{ // Create EnemyTop atlas
AlienAtals = new CAtlas();
AlienSprite = new CSprite();
*AlienAtals = *other.AlienAtals;
*AlienSprite = *other.AlienSprite;
}
Invader::~Invader()
{
for (std::list<Bullet*>::iterator it = Bullets->begin(); it != Bullets->end(); ++it)
delete *it;
delete Bullets;
delete AlienAtals;
delete AlienSprite;
}
void Invader::Init(InvaderTypes invadertype, CIw2DImage *AlienImage)
{
if (invadertype == InvaderTypes::TOP_ALIEN)
{
//SetAlienImage(AlienImage);
mImageAlien = AlienImage;
// Create EnemyTop atlas
int frame_w = (int)(mImageAlien->GetWidth() / 2);
int frame_h = (int)(mImageAlien->GetHeight());
AlienAtals = new CAtlas(frame_w, frame_h, 2, mImageAlien);
AlienSprite = new CSprite();
AlienSprite->m_X = 0;
AlienSprite->m_Y = 0;
AlienSprite->SetAtlas(AlienAtals);
AlienSprite->m_W = (float)AlienAtals->GetFrameWidth();
AlienSprite->m_H = (float)AlienAtals->GetFrameHeight();
AlienSprite->m_AnchorX = 0.5;
AlienSprite->SetAnimDuration(2);
}
else if (invadertype == InvaderTypes::MIDDLE_ALIEN)
{
}
else if (invadertype == InvaderTypes::LAST_ALIEN)
{
}
Visible = true;
Bullets = new std::list<Bullet*>();
Canfire = true;
}
I added the objects by doing:
list<Invader> invaders;
int spacing = 10;
for (int i = 0; i < 5; i++)
{
Invader invader;
invader.Init(TOP_ALIEN, gameResources->getAlienImageTop());
invader.AlienSprite->m_X = 50 + spacing;
invaders.push_back(invader);
spacing += 50;
}
the usage: Right now it causes an access violation when accessing (it)
for (list<Invader>::iterator it = invaders.begin(); it != invaders.end(); it++)
{
(it)->Update(FRAME_TIME);
(it)->Render();
}
You can see the result here in the following image:
First, during Invader::Init()), you provide a copy of the pointer to AlienImage. Then when you copy the object into the std::list, you don't copy that same pointer to the new object. Further, during Invader::Invader(const Invader& other), you are not making a copy of Invader::Bullets. This leaves both the values uninitialized causing Undefined Behavior upon access (which probably occurs in (it)->Update(FRAME_TYIME); and/or (it)->Render(); -- I can't verify since you didn't provide a MCVE.
Related
I'm trying to write the Space Invaders clone for Windows console, and it felt like everything was going fine, but here comes this crash. The program compiles without errors, however instantly crashes on start up.
I admit that I don't know pointers very well, and I believe that the problem is somewhere there.
#include "stdafx.h"
#include <vector>
#include <random>
#include <chrono>
#include <thread>
#include <memory>
#include <SDKDDKVer.h>
#include "Vector2D.h"
#include "Renderer.h"
std::default_random_engine rGen;
typedef std::uniform_int_distribution<int> intRand;
typedef std::uniform_real_distribution<float> floatRand;
char ObjectType[][64] =
{
"ot_AlienShip",
"ot_PlayerShip",
"ot_AlienLaser",
"ot_PlayerLaser",
"ot_Explosion"
};
class PlayField;
class GameObject
{
public:
char* m_objType = nullptr;
Vector2D pos;
unsigned char sprite;
virtual void Update(PlayField& world) {};
virtual bool DecreaseHealth() { return true; };
};
class Input
{
public:
virtual bool Left() = 0;
virtual bool Right() = 0;
virtual bool Fire() = 0;
};
class RndInput : public Input
{
public:
virtual bool Left() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.3f); }
virtual bool Right() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.4f); };
virtual bool Fire() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.5f); };
};
class PlayField
{
private:
typedef GameObject* GameObjPtr;
std::vector<GameObjPtr> gameObjects;
public:
Input* cotrollerInput = nullptr;
GameObject* it = new GameObject;
//it = new GameObject;
Vector2D bounds;
// Number of available active laser slots for aliens and player
int AlienLasers = 10;
int PlayerLasers = 4;
explicit PlayField(Vector2D iBounds) : bounds(iBounds) {};
const std::vector<GameObjPtr>& GameObjects() { return gameObjects; }
void Update()
{
// update list of active objects in the world
for (auto it : gameObjects)
{
it->Update(*this); //The crash is here "Unhandled exception thrown: read access violation. it was 0xFFFFFFFFFFFFFFFF."
}
}
GameObject* GetPlayerObject()
{
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [](GameObjPtr in) { return (strcmp(in->m_objType,"ot_PlayerShip")==0); });
if (it != gameObjects.end())
return (*it);
else
return nullptr;
}
void SpawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers--;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers--;
AddObject(newObj);
}
void DespawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers++;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers++;
RemoveObject(newObj);
}
void AddObject(GameObject* newObj)
{
//gameObjectsToAdd.push_back(GameObjPtr(newObj));
gameObjects.push_back(newObj);
}
void RemoveObject(GameObject* newObj)
{
//gameObjectsToRemove.push_back(newObj);
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [&](GameObjPtr in) { return (in==newObj); });
gameObjects.erase(it);
}
};
class Explosion : public GameObject
{
public:
// Explosion lasts 5 ticks before it dissappears
int timer = 5;
Explosion() { m_objType = new char[64]; strcpy(m_objType, "ot_Explosion"); sprite = RS_Explosion; }
~Explosion() { delete[] m_objType; }
void Update(PlayField& world) override
{
timer--;
if (!timer)
{
world.RemoveObject(this);
delete this;
}
}
};
class AlienLaser : public GameObject
{
public:
AlienLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienLaser"); sprite = RS_AlienLaser; }
~AlienLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y += 1.f;
if (pos.y > world.bounds.y)
{
deleted = true;
}
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
deleted = true;
//Spawn explosion, kill player
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
if (deleted)
{
world.DespawnLaser((GameObject*)this);
delete this;
}
}
};
class PlayerLaser : public GameObject
{
public:
PlayerLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerLaser"); sprite = RS_PlayerLaser; }
~PlayerLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y -= 1.f;
if (pos.y < 0)
{
deleted = true;
}
for (auto it : world.GameObjects())
{
if (strcmp(it->m_objType,"ot_AlienShip")==0 && it->pos.IntCmp(pos))
{
deleted = true;
//Spawn explosion, kill the alien that we hit
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
if (it->DecreaseHealth())
world.RemoveObject(it);
}
}
if (deleted)
{
world.DespawnLaser(this);
delete this;
}
}
};
class Alien : public GameObject
{
public:
Alien() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienShip"); sprite = RS_Alien; }
~Alien() { delete m_objType; }
private:
const float maxUpdateRate = 0.01f;
const float transformEnergy = 1.f;
enum AlienState
{
as_Normal,
as_Better
};
// Variables dictating energy level for upgrade, direction of movement, and current speed
float health = 1.f;
float energy = 0.f;
float direction = 1.f;
float velocity = 0.5f;
AlienState state{};
void Transform()
{
state = as_Better;
sprite = RS_BetterAlien;
velocity *= 2.f;
}
bool DecreaseHealth() override { health -= 1.f; return health <= 0; }
void Update(PlayField& world) override
{
pos.x += direction * velocity;
// Border check
if (pos.x < 0 || pos.x >= world.bounds.x - 1)
{
direction = -direction;
pos.y += 1;
}
// Border check vertical:
if (pos.y >= world.bounds.y - 1)
{
// kill player
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
//Spawn explosion
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
}
// Transform into better Alien
if (state!=as_Better)
{
floatRand updateRate(-maxUpdateRate, 2*maxUpdateRate);
energy += updateRate(rGen);
if (energy >= transformEnergy)
Transform();
}
floatRand fireRate(0, 1);
if (fireRate(rGen) < 0.5 && world.AlienLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new AlienLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
class PlayerShip : public GameObject
{
public:
PlayerShip() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerShip"); sprite = RS_Player; }
~PlayerShip() { delete m_objType; }
void Update(PlayField& world) override
{
if (world.cotrollerInput->Left())
pos.x -= 1;
else if (world.cotrollerInput->Right())
pos.x += 1;
if (world.cotrollerInput->Fire() && world.PlayerLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new PlayerLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
int main()
{
rGen.seed(1);
Vector2D size(80, 28);
Renderer mainRenderer(size);
PlayField world(size);
intRand xCoord(0, (int)size.x-1);
intRand yCoord(0, 10);
// Populate aliens
for (int k = 0; k < 20; k++)
{
Alien& a = *(new Alien);
a.pos.x = (float)xCoord(rGen);
a.pos.y = (float)yCoord(rGen);
world.AddObject(&a);
}
// Add player
PlayerShip& p = *(new PlayerShip);
p.pos = Vector2D(40, 27);
world.AddObject(&p);
world.Update();
{
RenderItemList rl;
for (auto it : world.GameObjects())
{
RenderItem a = RenderItem(Vector2D(it->pos), it->sprite);
rl.push_back(a);
}
mainRenderer.Update(rl);
// Sleep a bit so updates don't run too fast
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 0;
}
In addition, VS gives the following info:
I assume, that the pointer (or the object) appears to be cleared somewhere before, but I have no idea how to trace it. Thanks in advance.
The various ::Update methods you have add and remove objects from gameObjects while you are traversing it in Playfield::Update. This is a guaranteed crash as it invalidates the implicit iterator in your for loop.
To solve this, either:
Have GameObject::Update return a boolean that signifies if the object can be removed or not. This requires you to rewrite the loop in Playfield::Update with explicit iterators so you an do it = gameObjects.erase(it);. This will still not allow you to add new objects, though.
Defer additions and removals until the end of the game loop. Add a markForAddition / markForRemoval method that will remove these objects from the game world after Playfield::Update has gone through. You will need to do some extra bookkeeping to make sure you do not update or draw objects that have been removed earlier in the same loop, but that is surmountable.
Switch data structures: a std::list does not invalidate its iterators if you remove an object somewhere. You do still need to be careful with the current element though.
I am a beginner in threading with C++. Tried to create a thread t1 inside the class, but I want it not to be initialized. For this I did:
In the class variable, thread *t1 as if were to declare the class variable without initializing it.
Then from the constructor, I did:
t1 = new Thread(functionName);
But, there's some error that I didn't get. I previously had simple working experience with threading in Java.
Will this strategy cause an interlocking problem, assuming the solution exists to the way I want to implement it. (if I want to access the same variable without modifying it). Code goes like this:
class game:protected gameObjects
{
thread *t1;
///-------------------- Variables-------------------///
char keyPressed = NULL;
/// structure for holding player information.
struct player
{
int x, y0, y1,color;
};
player player1, player2;
/// for ball
int ballX, ballY, ballColor, ballBorderColor;
///--------------------------------------------------///
void initializeData()
{
ballX = 42;
ballY = 130;
player1.color = LIGHTCYAN;
player1.x = 30;
player1.y0 = 100;
player1.y1 = 200;
player2.color = LIGHTMAGENTA;
player2.x = 600;
player2.y0 = 100;
player2.y1 = 200;
/// Basic setUp For Game and Game Screen
ballBorderColor = RED;
ballColor = GREEN;
}
void updateScoreBoard()
{
/// whole score board border.
drawRect(20,10,620,50,RED);
/// left score board
drawRect(21,11,321,49,CYAN);
setfillstyle(SOLID_FILL,CYAN);
floodfill(30,40,CYAN);
setbkcolor(CYAN);
setcolor(LIGHTGREEN);
outtextxy(35,20,"Score:0");
///right score board.
drawRect(323,11,619,49,LIGHTMAGENTA);
setfillstyle(SOLID_FILL,LIGHTMAGENTA);
floodfill(330,40,LIGHTMAGENTA);
setbkcolor(LIGHTMAGENTA);
setcolor(LIGHTGREEN);
outtextxy(350,20,"Score:1");
}
void ballPosition()
{
setfillstyle(SOLID_FILL,ballColor);
drawCircle(ballX,ballY,10,ballBorderColor); ///ball for game
floodfill(ballX,ballY,ballBorderColor);
}
void playerBatPosition()
{
drawLine(player1.x,player1.y0,player1.x,player1.y1,player1.color);
drawLine(player2.x,player2.y0,player2.x,player2.y1,player2.color);
}
void setBackground()
{
setfillstyle(SOLID_FILL,background);
drawRect(0,0,getmaxx(),getmaxy(),RED);
floodfill(getmaxx()/2,getmaxy()/2,RED);
drawRect(20,60,620,470,WHITE); ///white line.
}
void updateScreenActivity()
{
playerBatPosition();
}
void startPlaying()
{
do
{
keyPressed = _getch();
if(keyPressed == 'w')
{
if(player1.y0 > 60)
{
drawLine(player1.x,player1.y0,player1.x,player1.y1,background);
player1.y0-=5;
player1.y1-=5;
}
}
else if(keyPressed == 's')
{
if(player1.y1 < 470)
{
drawLine(player1.x,player1.y0,player1.x,player1.y1,background);
player1.y0+=5;
player1.y1+=5;
}
}
if(keyPressed == 't')
{
if(player2.y0 > 60)
{
drawLine(player2.x,player2.y0,player2.x,player2.y1,background);
player2.y0-=5;
player2.y1-=5;
}
}
else if(keyPressed == 'g')
{
if(player2.y1 < 470)
{
drawLine(player2.x,player2.y0,player2.x,player2.y1,background);
player2.y0+=5;
player2.y1+=5;
}
}
updateScreenActivity();
}
while(keyPressed != 'q');
}
///-------------------Threading call --------------///
void startBallMovement(){
cout<<"Hello world"<<endl;
}
///-----------------------------------------------///
public:
game()
{
cleardevice();
initializeData();
setBackground();
updateScoreBoard();
playerBatPosition();
ballPosition();
startPlaying();
t1 = new thread(startBallMovement);
}
};
What I want to do is create a movement of a circle in different paths from the thread. I might sometimes need to access the variables from the thread to simulate the movement in different directions as per the user's strategy.
Error:
Your class implementation looks a bit lousy. But try something like this.
class game
{
private: thread* t;
public:
game()
{
t = new thread([this]() {startBallMovement();} );
}
~game()
{
if(t != nullptr)
{
t->join();
delete t;
}
}
void startBallMovement()
{
}
};
I am a little bit stumped on this at the moment, From what i understand, the specific enemies inherit from the base enemy class, so when created they use the base enemy constructor, to create their own objects copies of the variables to work with. so when i use the inherited enemy's set_E function wound`t that set its own E_Damage and E_Health to work with? and if so why is it denying access to its own variables?
In the locals tab on visual basic each x object does indeed have its own enemy variables, if i try direct access to the enemy class i get an access error, if i try use the objects own i also get this error, what am i doing wrong?
Base enemy class
class Enemy
{
public:
Enemy();
~Enemy();
void set_E(float, float);
float get_E_H();
float get_E_D();
virtual void Battle() = 0;
float* E_Damage;
float* E_Health;
char* E_Descrip;
float* E_Attack;
};
Enemy distructor
Enemy::~Enemy()
{
if (E_Damage != nullptr)
{
delete E_Damage;
E_Damage = nullptr;
}
if (E_Health != nullptr)
{
delete E_Health;
E_Health = nullptr;
}
if (E_Attack != nullptr)
{
delete E_Attack;
E_Attack = nullptr;
}
if (E_Descrip != nullptr)
{
delete E_Descrip;
E_Descrip = nullptr;
}
}
Enemy Constructor:
E_Damage = new float;
E_Damage = nullptr;
E_Health = new float;
E_Health = nullptr;
E_Attack = new float;
E_Attack = nullptr;
Example inherited enemy.h
class T_Rex : private Enemy, private CS_Text_Adventure
{
public:
void T_Rex::set_E(float Dmg, float Health, T_Rex x);
T_Rex();
~T_Rex();
void Battle() override;
};
Example inherited enemy.cpp
void T_Rex::Battle()
{
T_Rex x;
x.set_E(15.0f, 100.0f, x);
E_Descrip = "the passage way opens up to a tropical forrest";
std::cout << std::endl;
std::cout << x.E_Descrip << std::endl;
std::cout << std::endl;
while(*fight == true)
{
*x.E_Attack = rand() % 100 + 0;
if(*x.E_Attack >= 30)
{
*P_Health -= *x.E_Damage;
}
else if(*x.E_Attack < 30)
{
*x.E_Health -= *P_Health;
}
if(*P_Health <= 0)
{
*gamerun = false;
}
else if(*E_Health <= 0)
{
*fight = false;
}
}
step++;
}
Unauthorized access on the pointer deference reassign:
void T_Rex::set_E(float Dmg, float Health, T_Rex x)
{
*x.E_Damage = Dmg;
*x.E_Health = Health;
}
Lets take these two lines from your constructor:
E_Damage = new float;
E_Damage = nullptr;
First you allocate a single float, and make E_Damage point to it. Then you immediately make E_Damage a null pointer, so it no longer points anywhere valid. This leads to a memory leak.
Later you use this null pointer, which leads to undefined behavior and possible crashes.
For single values there's seldom a need to use pointers at all, especially in this case. Just declare E_Damage (and the other member variables) as normal non-pointer variables.
I have some code which works ok on Windows. It segfaults on linux. When I replace the offending delete with free, it seems ok. I'm relatively new to linux, what would you recommend to debug this? I am really missing VS right now...
Here's the offending code,
#include "qtree.hh"
int main(int argc, char* argv[])
{
point a(-3, 3);
point b(3, -3);
Node* pRoot = new Node(a, b);
pRoot->addChild(se);
Node::freeTree(pRoot);
return 0;
}
freeTree() is the method that segfaults.
qtree.hh
#ifndef _QTREE_HH_
#define _QTREE_HH_
#include <cmath>
#include "Body.hh"
#include "stdio.h"
#include "stdlib.h"
enum quadrant {ne, se, sw, nw};
struct point
{
double x;
double y;
point() {}
point(double xarg, double yarg)
{
x = xarg;
y = yarg;
}
};
class Node{
public:
Node(point nw, point se);
~Node();
void addBody(const Body& body);
void addChild(quadrant q);
static void freeTree(Node* pNode);
private:
point nwPoint;
point sePoint;
point comPoint;
double mass;
double dim;
Body* pBody;
Node* nwNode;
Node* neNode;
Node* swNode;
Node* seNode;
bool bIsLeaf;
};
#endif
qtree.cc
#include "qtree.hh"
FILE* fpTree;
const bool dbg = true;
Node::Node(point nw, point se)
{
nwPoint = nw;
sePoint = se;
mass = 0;
pBody = 0;
dim = std::abs(sePoint.x - nwPoint.x);
nwNode = 0;
neNode = 0;
swNode = 0;
seNode = 0;
bIsLeaf = true;
if (dbg && !fpTree)
{
fpTree = fopen("qtree.txt", "w");
}
}
Node::~Node()
{
//close file
if (fpTree) {fclose(fpTree);}
}
void Node::addChild(quadrant q)
{
point nwP = this->nwPoint;
point seP = this->sePoint;
this->bIsLeaf = false;
switch (q)
{
case ne:
{
nwP.x = (this->sePoint.x + this->nwPoint.x)/2;
seP.y = (this->sePoint.y + this->nwPoint.y)/2;
neNode = new Node(nwP, seP);
break;
}
case se:
{
nwP.x = (this->sePoint.x + this->nwPoint.x)/2;
nwP.y = (this->nwPoint.y + this->sePoint.y)/2;
seNode = new Node(nwP, seP);
break;
}
case sw:
{
seP.x = (this->nwPoint.x + this->sePoint.x) / 2;
nwP.y = (this->nwPoint.y + this->sePoint.y)/2;
seNode = new Node(nwP, seP);
break;
}
case nw:
{
seP.x = (this->nwPoint.x + this->sePoint.x) / 2;
seP.y = (this->sePoint.y + this->nwPoint.y) / 2;
nwNode = new Node(nwP, seP);
break;
}
}
if (fpTree)
{
fprintf(fpTree, "adding child of width %f to %s corner of parent",
(this->dim)/2,
(q == nw) ? "nw" :
(q == ne) ? "ne" :
(q == se) ? "se" :
(q == sw) ? "sw" : "invalid");
}
}
void Node::addBody(const Body& body)
{
}
//will free whole tree if arg is root
//recursively free all children then free self
void Node::freeTree(Node* pNode)
{
if (pNode)
{
if (pNode->neNode)
{
if (pNode->neNode->bIsLeaf) {delete(pNode->neNode);}
else {freeTree(pNode->neNode);}
}
if (pNode->seNode)
{
if (pNode->seNode->bIsLeaf) {delete(pNode->seNode);}
else {freeTree(pNode->seNode);}
}
if (pNode->swNode)
{
if (pNode->swNode->bIsLeaf) {delete(pNode->swNode);}
else {freeTree(pNode->swNode);}
}
if (pNode->nwNode)
{
if (pNode->nwNode->bIsLeaf) {delete(pNode->nwNode);}
else {freeTree(pNode->nwNode);}
}
delete pNode;
}
}
The deletes seem to cause a problem. Free is ok. Someone is probably going to tell me not to pair free with new, but I'm out of my element and just trying different things.
The destructor of the first node closes the debug file.
The destructor of the next node closes it again. This is illegal, and will likely crash.
While we're at it, ditch the freeNode function and move the destruction to the destructor where it belongs. The destructor should look like this:
Node::~Node()
{
delete nwNode;
delete swNode;
delete neNode;
delete seNode;
}
That's it. No need to check for null pointers or bIsLeaf.
Better yet, use std::unique_ptr, and ditch the destructor altogether (the rule of zero. google it).
I am wondering how to sort an array that contains objects of a custom class. I am trying to apply different sorting algorithms but in the swapping something goes wrong.
Here is my Code:
class RaceCar
{
private:
char* _brand;
char* _model;
double _price;
int _horse_power;
public:
//Other code
RaceCar(const RaceCar& rc):_price(rc._price), _horse_power(rc._horse_power)
{
_brand = new char[strlen(rc._brand)+1];
strcpy(_brand, rc._brand);
_model = new char[strlen(rc._model)+1];
strcpy(_model,rc._model);
}
RaceCar& operator=(const RaceCar& rc)
{
if(this != &rc)
{
delete _brand;
delete _model;
_brand = new char[strlen(rc._brand)+1];
strcpy(_brand, rc._brand);
_model = new char[strlen(rc._model)+1];
strcpy(_model, rc._model);
_price = rc._price;
_horse_power = rc._horse_power;
}
return *this;
}
bool operator<(const RaceCar& rc)
{
return (this->_price/this->_horse_power) > (rc._price/rc._horse_power);
}
//Other code
};
And this is the class that contains an array of RaceCars. I am trying to implement SortCars() method that orders the RaceCar objects inside the array of cars:
class RaceCarGarage
{
private:
RaceCar* _cars;
int _max_cars;
int _curr_occupied;
public:
RaceCarGarage():_cars(NULL), _max_cars(0),_curr_occupied(0){}
RaceCarGarage(const RaceCar& car, int max_cars)
:_max_cars(max_cars), _curr_occupied(0)
{
_cars = new RaceCar[_max_cars];
}
~RaceCarGarage()
{
delete _cars;
}
void AddCar(const RaceCar& car)
{
if(_curr_occupied < _max_cars)
{
_cars[_curr_occupied] = car;
_curr_occupied += 1;
}
}
void DisplayCars()
{
if(_curr_occupied > 0)
{
for(int i=0 ; i<_curr_occupied ; i++)
{
cout<<(i+1)<<". ";
(_cars+i)->Display();
}
}
}
void SortCars()
{
if(_curr_occupied > 1)
{
for(int i=0 ; i<_curr_occupied ; i++)
{
for(int j = i+1 ; j<_curr_occupied ; j++)
{
if(_cars[j]<_cars[i])
{
RaceCar buffer = _cars[i];
_cars[i] = _cars[j];
_cars[j] = buffer;
}
}
}
}
}
};
The problem with the swapping is that you use the traditional way to do:
temp = a // operator=
a = b // operator=
b = temp; // operator=
However, if you write:
RaceCar temp = a; // Copy constructor gets called (see comment on standard)
a = b; // operator=
b = temp; // operator=
The default copy constructor, just copies member by member, so just copies your pointer. So at the end, your temp and your will try to delete twice the same object pointed to.
Remark on assignment initializer :
For a type T, a statement in form T a = b; is an initializer.
The ISO standard C++ in section 12.6.1 point 1 explains "a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply;"