boost::ptr_vector questions - c++

I want to have a boost::ptr_vector of polymorphic pointers to objects (got this part mostly working), but I am having trouble with some functionality of it.
I want to load the ptr_vector using a case switch to determine what child type to point to so I don't have to have multiple pointers just floating around, but when I overwrite the type the pointer is polymorphically pointing to it overwrites everything in the ptr_vector that was pushed using the same pointer (I think this has to do with it is storing the exact pointer) which I think can be resolved by giving it copies/clones of the objects instead of just the pointer, but I don't see where, or how to tell it how to do this.
another problem that I am having is that when the scope the pointers existed in ends all the objects in the ptr_vector stop existing (I have a feeling this can be resolved by fixing the problem is paragraph2)
another question that I have is how to go about removing a specific object in the ptr_vector for example I find that I need to remove object 5 when there are 30+ objects in the ptr_vector (keep in mind that I will be searching for the objects to be removed)
EDIT: I have already implemented the clone methods, and they are working correctly
Edit: example code
#include <iostream>
#include <string>
#include <vector>
#include <boost/ptr_container/ptr_vector.hpp>
gameObject (polymorphic types)
class GameObject {
public :
bool toBeRemoved;
char Type;
int name;
virtual ~GameObject(){}
virtual void updateObject(float duration){}
virtual GameObject * clone(void)const = 0;
};
class Entity : public GameObject{
public:
Entity(int _name){
Type = 'e'; name = _name;
toBeRemoved = false;
}
Entity(const Entity & _o){
toBeRemoved = _o.toBeRemoved;
}
~Entity(){}
void updateObject(float duration){
if(!toBeRemoved){
// perform update work
}
}
Entity * clone(void)const{return new Entity(*this);}
};
class Wall:public GameObject{
public:
bool destroyable;
Wall(bool destroy, int _name){
Type = 'w'; name = _name;
destroyable = destroy;
toBeRemoved = false;
}
Wall(const Wall & _o){
destroyable = _o.destroyable;
}
void updateObject(float duration){
if((duration > 10)&&(destroyable)){
toBeRemoved = true;
}
}
~Wall(){}
Wall * clone(void)const{return new Wall(*this);}
};
object Manager (to give example of scoping)
typedef boost::ptr_vector<GameObject*> things; // not sure how to tell this how to clone the objects, or if I have to do it manually
class ObjectMgr{
things objects;
public:
ObjectMgr(){
int entityCount = 0;
int wallCount = 0;
std::vector<std::vector<char>> map;
map.resize(3);
for(int ii = 0; ii <3 ; ii++){map[ii].resize(6);}
for(int ii = 0; ii<6; ii++){map[0][ii] = 'w';}
map[1][0] = 'w'; map[1][1] = 'E';
map[1][2] = '.'; map[1][3] = 'D';
map[1][4] = 'E'; map[1][5] = 'D';
for(int ii = 0; ii<6; ii++){map[2][ii] = 'w';}
GameObject* currentObject;
for(int zz = 0; zz < map.size(); zz++){
for(int xx = 0; xx < map[zz].size(); xx++){
switch(map[zz][xx]){
case 'w': // undestroyable wall
currentObject = new Wall(false,wallCount++);
objects.push_back(&currentObject);
break;
case 'E': // entity
currentObject = new Entity(entityCount++);
objects.push_back(&currentObject);
break;
case 'D': // destroyable wall
currentObject = new Wall(true,wallCount++);
objects.push_back(&currentObject);
break;
case '.': // empty space
break;
}
}
}
print();
}
void clean(){ // thinking that might need to do some kind of swapping, and then maybe pop_back()
bool removed;
do{
removed = false;
things::iterator ii = objects.begin();
// for(int jj = 0; ii != objects.end(); ii++, jj++){
// if(objects[jj]->toBeRemoved == true){
// removed = true;
// // remove item here
// break;
// }
// }
} while(removed);
}
void print(){
int count = 0;
things::iterator reg = objects.begin();
std::cout << "entities" << std::endl << "===================" << std::endl;
for(int ii = 0; reg!=objects.end(); ii++, reg++){
std::cout << count++ << ": " << objects[ii]->Type << objects[ii]->name << " " << std::endl;
}
std::cout << "====================" << std::endl << "/entities" << std::endl;
}
};
main
int _tmain(int argc, _TCHAR* argv[]){
ObjectMgr manager;
std::cout << "constructed now displaying" << std::endl;
manager.print();
std::cout << "cleaning" << std::endl;
// manager.clean();
std:: cout << "displaying after cleaning" << std::endl;
// manager.print();
return 0;
}

Related

How can I delete a vector element if I have a pointer to it, not an iterator?

I'm having an issue with a child object needing its parent to destroy it. I want something like the following, it's just an example:
#include <iostream>
#include <vector>
struct Object
{
Object(Object* parent) : parent(parent) {}
Object* parent;
std::vector<Object*> children;
bool flag = false;
void update() { if (flag) parent->deleteChild(this); } // Or mark it for deletion afterwards
void deleteChild(Object* child) { delete child; /*children.erase(/* I need the iterator here);*/ }
};
int main()
{
Object* parent = new Object(nullptr);
for (int i = 0; i < 100; ++i) parent->children.push_back(new Object(parent));
parent->children[42]->flag = true;
for (auto i : parent->children) i->update();
return 0;
}
If I keep track of the child's position in the vector I know how to do it, but I basically want to know how I can erase an element of a vector if I have a pointer to it.
Edit: AndyG was right all along, I can't do what I'm wanting because my Objects are all over the place in memory when I "new" it. I did manage to do it another way using placement new, creating the objects all in one contiguous buffer, but it's definitely not worth the trouble. I did learn a lot though.
#include <iostream>
#include <vector>
struct Object
{
Object(Object* parent, int position) : parent(parent), numberPosition(position)
{
std::cout << "Constructing object number: " << numberPosition << " at at heap memory location: " << this << '\n';
}
Object* parent;
int numberPosition = 0;
std::vector<Object*> children;
bool flag = false;
void update()
{
if (flag) parent->deleteChild(this);
}
void deleteChild(Object* child)
{
Object* pChild = &(*child);
ptrdiff_t position = child - *children.data();
std::vector<Object*>::iterator it = children.begin() + position;
std::cout << "About to delete vector element at position: " << (*it)->numberPosition << '\n';
// delete pChild; Not supposed to deallocate each placement new. See http://www.stroustrup.com/bs_faq2.html#placement-delete and https://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new
std::cout << "Size of children vector = " << children.size() << '\n';
children.erase(it);
std::cout << "Size of children vector = " << children.size() << '\n';
}
~Object() { std::cout << "Destroying object number " << numberPosition << '\n'; }
};
int main()
{
Object* parent = new Object(nullptr, 0);
char* contiguousBuffer = static_cast<char*>(malloc(100 * sizeof(Object)));
for (int i = 0; i < 100; ++i)
{
Object* newAddress = new (contiguousBuffer + i * sizeof(Object)) Object(parent, i); // Placement new
parent->children.push_back(newAddress);
}
parent->children[42]->flag = true;
//for (auto i : parent->children) i->update(); // Iterator gets invalidated after erasing the element at 42 doing it this way
for (int i = 0; i < parent->children.size(); ++i) parent->children[i]->update();
free(contiguousBuffer);
// Destructors also need to be called
return 0;
}
Unfortunately the only way to do it is to search through the vector like normal.
auto it = std::find(std::begin(children), std::end(children), child);
if (it != std::end(children)){
children.erase(it);
delete child;
}
Demo
Assuming the vector is not required to be sorted, then we can swap the child element to the end and then resize the vector.
This method does not need to move all the element of the vector between child and the last element.
auto it = std::find(std::begin(children), std::end(children), child);
if (it != std::end(children)){
std::iter_swap(children.rbegin(), it);
children.resize(children.size() - 1);
delete child;
}

For loop and vectors causing trouble

I am trying to create a for loop that runs through a vector, pushes something into a queue, updates all appropriate elements of the struct of the vector and then loops again until the vector is empty.
The problem I am having is that the function to run through my for loop does not seem to be updating my vector elements, I am getting the same output even though I know it should be changing.
Here is the link to a gist of the project https://gist.github.com/sgodfrey321/6cffd85896432b2942aa , it just has a lot of filler, I had to input alot of stuff by hand so it is kinda messy.
So to start with I pass my vector to the function in a while loop
void queueNodes(vector<vertex>& list, queue<vertex>& q);
as such
int counter = 0;
while (counter < 11) {
queueNodes(nodes, q);
counter++;
}
the while loop is to make sure that I run through the vector enough times. Now in the function I check to see if incomingEdges of the struct is 0 and if it is I push it into the queue and update the next door incomingEdges:
void queueNodes(vector<vertex>& nodes, queue<vertex>& q) {
for (auto i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
q.push(i);
cout << "foo";
} else {
cout << "bar";
}
}
Now I would expect that when I use the function again with the updated vector I would see a change in output as updating the next door incomingEdges would cause some to trigger the condition I am looking for. I have written outputs that show the next door incomingEdges are in fact decreasing however I can not seem to use the updated vector in the function call.
Do I need to return the vector somehow? I am sure that I am calling the function incorrectly, but does anyone have any ideas?
Thank You
edit: forgot to pass by reference the queue!
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct vertex {
char vertexName;
int incomingEdges;
vertex* nextDoorTop;
vertex* nextDoorMiddle;
vertex* nextDoorBottom;
};
void queueNodes(vector<vertex>& nodes);
int main() {
vertex s, A, G, D, B, H, E, C, I, F, t;
s.vertexName = 's';
s.incomingEdges = 0;
s.nextDoorTop = &A;
s.nextDoorMiddle = &D;
s.nextDoorBottom = &G;
A.vertexName = 'A';
A.incomingEdges = 2;
A.nextDoorTop = &B;
A.nextDoorMiddle = &E;
G.vertexName = 'G';
G.incomingEdges = 1;
G.nextDoorTop = &D;
G.nextDoorMiddle = &E;
G.nextDoorBottom = &H;
D.vertexName = 'D';
D.incomingEdges = 2;
D.nextDoorMiddle = &E;
B.vertexName = 'B';
B.incomingEdges = 1;
B.nextDoorTop = &C;
H.vertexName = 'H';
H.incomingEdges = 1;
H.nextDoorTop = &E;
H.nextDoorMiddle = &I;
E.vertexName = 'E';
E.incomingEdges = 4;
E.nextDoorTop = &C;
E.nextDoorMiddle = &F;
E.nextDoorBottom = &I;
C.vertexName = 'C';
C.incomingEdges = 3;
C.nextDoorMiddle = &t;
I.vertexName = 'I';
I.incomingEdges = 2;
I.nextDoorTop = &F;
I.nextDoorMiddle = &t;
F.vertexName = 'F';
F.incomingEdges = 2;
F.nextDoorMiddle = &t;
t.vertexName = 't';
t.incomingEdges = 3;
vector<vertex> nodes { s, A, G, D, B, H, E, C, I, F, t };
cout << "Vertex Name: " << " Number Of Edges: " << endl;
for (auto i : nodes) {
cout << i.vertexName << " " << i.incomingEdges << " "
<< endl;
}
int counter = 0;
while (counter < 11) {
queueNodes(nodes);
counter++;
}
return 0;
}
void queueNodes(vector<vertex>& nodes) {
for (auto& i : nodes) {
cout << endl << i.vertexName << " ";
if (i.incomingEdges == 0) {
i.nextDoorTop->incomingEdges--;
i.nextDoorMiddle->incomingEdges--;
i.nextDoorBottom->incomingEdges--;
cout << "foo";
} else {
cout << "bar";
}
}
}

Can't assign in an array of pointers

My problem is in adaugare function on this line I think persoane[numar_persoane] = pers. Because that line gives me this error. What problem do I have?
I have to use a dynamic array of pointers.
class baze
{
private: int numar_persoane=0;
persoana (*persoane)=(persoana *)malloc(0);
public: baze()
{
persoane = NULL;
}
~baze()
{
delete[] persoane; //???????????
}
void adaugare(persoana pers)
{
numar_persoane++;
realloc(persoane, sizeof(persoana)*numar_persoane);
persoane[numar_persoane] = pers;
};
void afisarealfa()
{
for (int i = 0; i < numar_persoane; i++)
for (int j = i + 1; j < numar_persoane; j++)
if (persoane[i].gnume()>persoane[i].gnume())
{
persoana aux;
aux = persoane[i];
persoane[i] = persoane[j];
persoane[j] = aux;
}
for (int i = 0; i < numar_persoane; i++)
{
cout << "Nume:" << persoane[i].gnume() << endl;
cout << "Varsta" << persoane[i].gan() << endl;
cout << "sex" << persoane[i].gsex();
}
}
This is persoana class:
class persoana
{
private: string nume;
int an;
char sex;
public: void snume(string numebagat)
{
nume = numebagat;
}
string gnume()
{
return nume;
}
void san(int anbagat)
{
an = anbagat;
}
int gan()
{
return an;
}
void ssex(char sexbagat)
{
sex = sexbagat;
}
char gsex()
{
return sex;
}
};
Main:
int _tmain(int argc, _TCHAR* argv[])
{
persoana a;
a.san(1990);
a.snume("Gogu");
a.ssex('m');
cout << "Nume: " << a.gnume() << endl << "Anul nasterii: " << a.gan() << endl << "Sex: " << a.gsex();
baze b;
b.adaugare(a);
return 0;
}
As #simpletron pointed out, malloc(0) is kind of weird. I wasn't even sure it was legal, but per what's the point in malloc(0)?, it is legal albeit implementation defined (it might be NULL, it might be a valid empty region; either way, you can call free on it, and per Is a malloc() needed before a realloc()? you can just skip that malloc;
Using malloc but then delete is not recommended; per Behaviour of malloc with delete in C++, this IS undefined behavior; you should probably be using free to go with realloc instead of delete
You have an off by one error. You should use persoane[numar_persoane-1] = pers;
You probably should set age/sex to some default value in the constructor.
This might be my personal preference, but I dislike copying classes around versus pointer to classes.

array of strings inside the object

I've got a problem with creating an array of strings inside the object. I don't know how to patch it around so I'm asking for help. Here lies the problem:
main.h:
#pragma once
#include <iostream>
#include <conio.h>
#include <string>
class tab2D {
protected:
int width;
int height;
string **sTab;
int **iTab;
public:
tab2D();
tab2D(int x, int y, char c);
~tab2D();
tab2D(tab2D&t);
};
class chess: public tab2D {
public:
chess(int x, int y);
~chess();
chess(chess&c);
void init();
bool ifMove();
void show();
};
class matrix: public tab2D {
public:
matrix(int x, int y);
~matrix();
matrix(matrix&m);
};
The compiler says: syntax error : missing ';' before '*' about the line
string **sTab;
I assume that I can't make the dynamic array of strings and it makes further problems with processing this array.. Can you help me? :)
*UPDATE 1*Thanks, I forgot to add line
using namespace std;
Now it works, but I've got another problem.
#include "main.h"
using namespace std;
////// Konstruktor, konstruktor kopiujący oraz destruktor //////
chess::chess(int x = 8, int y = 8) : tab2D(x, y, 'c') {
init();
};
chess::chess(chess&c) {
chess(c.width, c.height);
};
chess::~chess() {
};
////// Metody //////
////// Uzupełnianie kolorów pól oraz rozstawianie figur i pionków //////
void chess::init() {
/// kolory pól: 0 - biały, 1 - czarny///
int last = 0;
for(int i = 0; i < this->height; ++i) {
for(int j=0; j < this->width; ++j) {
if(last = 0) {
this->sTab[i][j] = "1";
last = 1;
}
else if(last = 1) {
this->sTab[i][j] = "0";
last = 0;
}
}
if(last = 0)
last = 1;
else if(last = 1)
last = 0;
};
/// rozstawienie pionków ///
for(int i = 0; i < this->width; ++i) {
sTab[1][i] = sTab[1][i] + "0";
sTab[6][i] = sTab[6][i] + "a";
};
};
////// Wyświetlenie szachownicy //////
void chess::show() {
for(int i = 0; i < (this->height + 1); ++i) {
for(int j=0; j < (this->width + 1); ++j) {
if(i == 0 && j == 0)
cout << " ";
else if (i != 0 && j == 0) {
switch (i) {
case 1:
cout << "A ";
break;
case 2:
cout << "B ";
break;
case 3:
cout << "C ";
break;
case 4:
cout << "D ";
break;
case 5:
cout << "E ";
break;
case 6:
cout << "F ";
break;
case 7:
cout << "G ";
break;
case 8:
cout << "H ";
break;
default:
break;
}
}
else if (i == 0 && j != 0) {
cout << j << " ";
}
else {
cout << this->sTab[i-1][j-1] << " ";
}
}
cout << endl;
};
};
When I run the program, there is a breakpoint in the line
this->sTab[i][j] = "0";
I assume there is something wrong with making the array of strings but I don't understand why exactly there is a breakpoint and can't debug it.
UPDATE 2
Here is the code for tab.cpp:
#include "main.h"
using namespace std;
////// Konstruktor domyślny, konstruktor, konstruktor kopiujący oraz destruktor //////
tab2D::tab2D() {
};
tab2D::tab2D(int x, int y, char c) {
this->width = x;
this->height = y;
if (c == 'm') {
this->iTab = new int*[this->width];
for(int i=0;i<this->height;++i)
this->iTab[i] = new int[this->width];
}
else if (c == 'c') {
this->sTab = new string*[this->width];
for(int i=0;i<this->height;++i)
this->sTab[i] = new string[this->width];
}
else {
}
};
tab2D::tab2D(tab2D&t) {
tab2D(t.width, t.height, 't');
};
tab2D::~tab2D() {
for(int i=0;i<height;++i)
delete [] iTab[i];
delete [] iTab;
for(int i=0;i<height;++i)
delete [] sTab[i];
delete [] sTab;
};
You need to qualify names from the standard library:
std::string **sTab;
^^^^^
If you're doing what I think you're doing and allocating things with new, then you should consider using std::vector to deal with the quagmire of memory management issues you're about to encounter. If you really want to juggle pointers yourself for some reason, don't forget the Rule of Three.
UPDATE Your new problem might be because the copy constructor is horribly broken:
chess::chess(chess&c) {
chess(c.width, c.height);
};
This creates and destroys a temporary object, but doesn't initialise the object being constructed, leaving it in an invalid state. You probably don't want to declare a copy-constructor at all, as long as the base class is correctly copyable. If you did need one, it should should be more like:
chess::chess(chess const & c) : // 'const' so constant objects can be copied
tab2D(c) // copy the base-class subobject
{
// do whatever else needs doing
}
Alternatively, the new problem might be due to errors in the tab2D constuctors which you haven't shown us. The best way to track it down is to step through the program with a debugger, checking that everything is correctly initialised before use.
UPDATE Probably, the runtime error is caused by allocating the wrong number of pointers. You want
iTab = new int*[height]; // Not width
and likewise for sTab.

Crashing when objects are deleted

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;
}