How to push_back an object properly? - c++

Hi I have an issue with pushing back object to a vector of objects.
class boxes {
protected:
int x = 0;
public:
void setx (int input) {
x = input;
}
int getx () {
return x;
}
} box;
box.setx(5);
vector <boxes> tetris;
tetris.push_back(box);
cout << box.getx();
cout << tetris.back().getx();
Why the two prints have different values? The first one is giving me x=5, the second x=0.

If you provide a copy constructor, make sure you implement it correctly. My bug was there.

Related

c++ initialize non pointer object again

In my header file I have:
class Game
{
private:
string _name;
Level _currentLevel;
public:
Game();
~Game();
void setName();
void run();
};
In my cpp file I have my run function:
void Game::run()
{
bool finished = false;
string input;
while (!finished)
{
// get input
std::cout << "Enter a command: \n";
std::getline(std::cin, input);
if (input == "quit")
{
finished = true;
}
else if (input == "new")
{
Level _currentLevel;
}
else if (input == "print")
{
_currentLevel.printMap();
}
else
{
std::cout << "Unknown command! \n";
}
}
}
constructor and printmap method of Level
Level::Level()
{
_width = RandomGenerator::Instance()->getRandom(6, 10);
_height = RandomGenerator::Instance()->getRandom(6, 10);
for (int y = 0; y < _height; y++)
{
for (int x = 0; x < _width; x++)
{
addRoom(x, y);
}
}
}
void Level::printMap()
{
for (int y = 0; y < _height; y++)
{
for (int x = 0; x < _width; x++)
{
if (x != 0)
cout << " - ";
cout << _map[coordinate(x, y)].getSize();
}
cout << "\n";
}
}
However when I type new, that runs Level _currentLevel; (to create a new non pointer object), the object dosnt change. I can see it dosn't change the values of level when I run printmap (which prints a map with 30 random values created in the Level constructor). While debugging the value of _height changes in the Level constructor. How should the value of _currentLevel be updated from my Game class?
Your new block creates a local stack variable that happens to have the same name as your instance variable (_currentLevel). It does not overwrite the instance variable, and that's why nothing changes.
You have a few straightforward choices:
Use a pointer. I suggest using a shared_ptr so you don't have to worry about deallocating memory on your own.
Extend Level to have an Initialize function. The constructor can call this, or you can call it from other code later if you want to re-initialize an existing variable.
Copy a new local variable to the instance variable.
Personally, I'd suggest the pointer, but either works.
For starters the prototypes should be in the header and the implementation in the source file. Apart from that, in game::run you declare a second local _currentlevel which shadows the class variable. Outside the constructor, you never modify the class field.
Replace the shadowing line with this->_currentlevel = Level();

Why does returning a data structure rather than a pointer mess with the integrity of my data?

I'm building a sparse matrix class that holds two arrays (row and column) of pointers to doubly linked lists (down and right). Sort of like this:
rows
c0123456789
o1
l2
u3
m4 A-->B-->
n5 | |
s6 | V
7 V D-->
8 C-->
9
Both arrays are initialized to have nullptr in every space until something is inserted in that place.
I have a function "readFile" that reads in objects from a text file and inserts them into this sparse matrix. For some reason, before this function returns, all of the data in it is fine, but after I return, I get random memory locations in my arrays. Here is main.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "sparseMatrix.h"
using namespace std;
class basic
{
private:
int x, y;
string word;
basic *down;
basic *right;
public:
basic(int x, int y, string word)
{
this->x = x;
this->y = y;
this->word = word;
down = nullptr;
right = nullptr;
}
int getX()
{
return x;
}
int getY()
{
return y;
}
basic *getRight()
{
return right;
}
void setRight(basic *newRight)
{
right = newRight;
}
basic *getDown()
{
return down;
}
void setDown(basic *newDown)
{
down = newDown;
}
void print()
{
cout << "X: " << x << ", Y: " << y << ", word: " << word << ".\n";
}
};
sparseMatrix<basic> readFileBROKEN(string pathToFile);
sparseMatrix<basic> *readFile(string pathToFile);
int main()
{
cout << "Working:\n\n";
sparseMatrix<basic> *workingMatrix = readFile("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
workingMatrix->printyArray();
cin.get();
cout << "Not working:\n\n";
sparseMatrix<basic> brokenMatrix = readFileBROKEN("C:/users/jmhjr/desktop/testdata.txt");
cout << "After returning, here are all the locations that are NOT nullptr:\n";
brokenMatrix.printyArray();
cin.get();
delete workingMatrix;
}
sparseMatrix<basic> readFileBROKEN(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> matrix(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix.insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix.printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
sparseMatrix<basic> *readFile(string pathToFile)
{
ifstream inputFile;
inputFile.open(pathToFile);
if (inputFile.fail())
{
cout << "Couldn't open " << pathToFile << "!\n";
exit(-1);
}
sparseMatrix<basic> *matrix = new sparseMatrix<basic>(100, 100);
while (!inputFile.eof())
{
int x, y;
string word;
inputFile >> x >> y >> word;
basic data(x, y, word);
matrix->insert(data);
}
cout << "Before returning, here are all the locations that are NOT nullptr:\n";
matrix->printyArray();
cout << "press ENTER to return\n";
cin.get();
return matrix;
}
and here is sparseMatrix.h:
template <class dataType>
class sparseMatrix
{
private:
//The dimensions of the sparse matrix.
int width;
int height;
//Dynamic array of pointers to heads of linked lists.
dataType** xArray;
dataType** yArray;
public:
//Constructor. Sets everything in the two arrays to nullptr.
sparseMatrix(int height, int width)
{
this->width = width;
this->height = height;
xArray = new dataType*[width];
yArray = new dataType*[height];
for (int row = 0; row < height; row++)
{
this->yArray[row] = nullptr;
}
for (int col = 0; col < width; col++)
{
this->xArray[col] = nullptr;
}
}
//Deconstructor. First goes through the matrix and looks for every city it can find, and deletes
//all of those. Then when it's done, it deletes the two dynamic arrays.
~sparseMatrix()
{
dataType *currentdataType;
dataType *next;
for (int row = 0; row < height; row++)
{
currentdataType = yArray[row];
while (currentdataType != nullptr)
{
next = currentdataType->getRight();
delete currentdataType;
currentdataType = next;
}
}
delete [] yArray;
delete [] xArray;
}
//Creates a copy of the data we are passed, then creates links to this copy.
void insert(dataType data)
{
//Make sure the data is valid.
if (data.getX() < 0 || data.getX() >= width || data.getY() < 0 || data.getY() >= height)
{
std::cout << "That dataType doesn't fit into the sparse matrix!\n";
data.print();
std::cin.get();
}
else
{
//Copy the data we were passed.
dataType *newData = new dataType(data);
//Easy case. If nothing is in this row, set yArray[row] to the address of this data.
if (yArray[data.getY()] == nullptr)
{
yArray[data.getY()] = newData;
}
//Not so easy case. Move forward (right) until we find the right location, then set links.
else
{
dataType *current = yArray[data.getY()];
while (current->getRight() != nullptr)
{
current = current->getRight();
}
current->setRight(newData);
}
//Easy case. If nothing is in this col, set xArray[col] to the address of this data.
if (xArray[data.getX()] == nullptr)
{
xArray[data.getX()] = newData;
}
//Not so easy case. Move forward (down) until we find the right location, then set links.
else
{
dataType *current = xArray[data.getX()];
while (current->getDown() != nullptr)
{
current = current->getDown();
}
current->setDown(newData);
}
}
}
void printyArray()
{
for (int r = 0; r < height; r++)
{
if (yArray[r] != nullptr)
{
std::cout << r << ' ';
//yArray[r]->print();
}
}
}
};
readFile reads everything in from a file that looks like this:
0 0 hello
5 2 world
6 8 foo
9 5 bar
...
As expected, before returning, the only locations that are NOT nullptr are the ones that I have inserted into. (0, 2, 8 and 5). However when the function returns, EVERY SINGLE location in the array is not nullptr. I added a second function which returns a pointer to dynamically allocated sparseMatrix object, rather then returning the object itself, and this fixed it. However, I don't understand why. It seems like these two functions should behave identically the same way.
Also, the part that is most confusing to me, why does this run perfectly fine in Xcode, but not in Visual Studio?
tomse's answer is correct and gives the why and a fix, but it's an unnecessarily expensive fix for this problem. His suggestion of the copy constructor also solves numerous future problems such as the classics Why did my vector eat my data? and Dude, where's my segfault? Make the copy constructor. Don't use it unless you have to.
I think Andras Fekete got the problem right, but his post is kind of garbled. His solution is bang on, though.
Define your function like this:
bool readFile(string pathToFile, sparseMatrix<basic> & matrix)
Remove the definition of matrix inside the function in favour of the one passed in.
Return false on error so you know the matrix is bad (or use exceptions).
Create the matrix in the calling function and pass it into the revised reader function.
sparseMatrix<basic> matrix(100, 100);
if readFile("C:/users/jmhjr/desktop/testdata.txt", matrix);
That puts you right back where you were with the pointer version, but without the pointer and without having to do the extra work of copying data you didn't need to copy.
Your function:
sparseMatrix<basic> readFileBROKEN(string pathToFile)
returns a copy of the object (which is OK), but sparseMatrix does not define a copy constructor, so the default generated will be used which creates a shallow copy by just copying the adresses inside the returned object.
But the memory where the address points to is deleted when you leave your function (because the destructor of the locally created object is called).
To solve this you have to define your own copy contructor in sparseMatrix which copies all the content of the object.
sparseMatrix(const sparseMatrix& rhs) :
width(rhs.width),
height(rhs.height),
xArray(nullptr),
yArray(nullptr)
{
... and now copy all the content from rhs.xArray to this->xArray,
(same for yArray)
}
The problem is that you're allocating 'matrix' inside both of the readFile functions. Upon returning from the function, both variables are deallocated. However, returning the value (eradFile) the matrix is copied into your variable of the calling function, whereas returning the pointer (readFileBROKEN) is just returning the address where the matrix used to be stored.
To fix this, you should allocate the 'matrix' variable, and pass in a reference to the function. Then the function can return a void while stuffing the matrix properly.

Static variable and object life span in C++

I'm a beginner in C++ and I understand basic concepts of "pass-by-value or reference", object scope and object instantiation with and without use of the keyword "new" in simple examples. The problem is that when the problems that I'm trying to solve become more complicated, I don't know how is this theory I know from simple examples applied in problems that consists of multiple classes.
I have a PaintWidget.cpp which is responsible for painting all the Vehicles.
void PaintWidget::paintEvent(QPaintEvent *) {
if (!Vehicle::GetVehicles()->empty()) {
cout << "not null" << endl;
QPainter painter(this);
QPen pen1(Qt::red);
pen1.setWidth(2);
std::vector<Vehicle>::iterator it;
for (it = Vehicle::GetVehicles()->begin(); it != Vehicle::GetVehicles()->end(); it++) {
cout << "draaaaw" << endl;
QRect rect(it->GetXcord(), it->GetYcord(), it->GetWidth(), it->GetHeight());
cout << std::to_string(it->GetXcord()) + " " + std::to_string(it->GetYcord()) + " " + std::to_string(it->GetWidth()) + " " + std::to_string(it->GetHeight()) + " " << endl;
painter.setPen(pen1);
painter.drawRect(rect);
}
} else {
cout << "is null" << endl;
}
}
And then I have Vehicle.h
#ifndef VEHICLE_H
#define VEHICLE_H
#include <vector>
#include <map>
class Vehicle {
public:
Vehicle();
Vehicle(const Vehicle& orig);
virtual ~Vehicle();
void initVehicles();
Vehicle createVehicle();
static std::map<Road, int> &GetVeh_num() {
return veh_num;
}
static std::vector<Vehicle> *GetVehicles() {
return &vehicles;
}
private:
int xcord;
int ycord;
int height = 20;
int width = 50;
static std::vector<Vehicle> vehicles;
static std::map<Road, int> veh_num;
};
#endif /* VEHICLE_H */
Vehicle.cpp
#include "Vehicle.h"
#include <vector>
std::vector<Vehicle> Vehicle::vehicles;
std::map<Vehicle::Road, int> Vehicle::veh_num = {
{Vehicle::Top, 0},
{Vehicle::Right, 0},
{Vehicle::Bottom, 0},
{Vehicle::Left, 0}
};
Vehicle::Vehicle() {
}
Vehicle::Vehicle(const Vehicle& orig) {
}
Vehicle::~Vehicle() {
}
int Vehicle::GetXcord() const {
return xcord;
}
int Vehicle::GetYcord() const {
return ycord;
}
void Vehicle::SetXcord(int xcord) {
this->xcord = xcord;
}
void Vehicle::SetYcord(int ycord) {
this->ycord = ycord;
}
int Vehicle::GetHeight() const {
return height;
}
int Vehicle::GetWidth() const {
return width;
}
void Vehicle::initVehicles() {
for (int i = 0; i < 5; i++) {
Vehicle::vehicles.push_back(this->createVehicle());
}
}
Vehicle Vehicle::createVehicle() {
std::map<Vehicle::Road, int>::iterator it;
Vehicle v;
for (it = Vehicle::veh_num.begin(); it != Vehicle::veh_num.end(); it++) {
int &vehnum = it->second;
if (it->first == Vehicle::Road::Right) {
int xc = 520 + vehnum * this->GetWidth() + vehnum * 5;
int yc = 220;
v.SetXcord(xc);
v.SetYcord(yc);
v.SetRoad(Vehicle::Right);
}
}
return v;
}
As you can see, createVehicle returns copy of a new Vehicle which is then inserted in the static variable Vehicles. GetVehicles returns pointer to vector of inserted vehicles because I don't want to return a copy. When I run this code, nothing gets painted although there are 5 objects in the static variable (paintEvent gets called and string "draaaaw" is printed 5 times). I suspected that I have a problem with object life span, so I changed
static std::vector<Vehicle> vehicles;
to
static std::vector<Vehicle*> vehicles;
and of course instantiation of Vehicle from
Vehicle v;
to
Vehicle *v = new Vehicle();
which creates an object on the heap if I understand correctly. After all required changes in methods, my code works (all objects are painted). What I don't understand is when these objects get destroyed and why, if I'm returning a copy every single time. How come vector vehicles is not empty (I still have 5 "ghost" objects that do not contain any values I set earlier). As far as I understand creating objects with new is not recommended, so the second option are smart pointers?
Thanks :)
Edit
I purposely left out setters and getters in .h and .cpp file so that the code is as short as possible with only relevant information.
I think your main problem is you have defined an empty copy constructor:
Vehicle::Vehicle(const Vehicle& orig)
{
}
This means that when you add a Vehicle to a container, a copy is made that doesn't actually copy anything. You should delete your own constructor and let the compiler do the work for you.
You will never have a problem with object life span if you don't use pointers or references. It simply isn't possible for that to occur. The whole concept of "RAII" is that if the variable is accessible in code, it is valid.
To test this, also print out the coordinates and size of your vehicle. If that prints a valid result, you know that your issue lies in the paint function itself.
So just don't pass a pointer of the object unless you need to modify it, and even then return a reference instead of a pointer. Just make sure not to return a reference of a temporary, as that CAN lead to life span issues.

C++ "No matching constructor for initializing Employee"

I am new to C++ and practicing using vector as an object. However, I got an error "No matching constructor for initializing Employee" when I tried running the following program.
Please tell me how I could modify my program!
Also, when I write
staff[0] = Employee{"Harry Potter" 55000};
does this mean that I am storing string and double data in one of 10 open boxes in vector object of type Employee?
I apologize for such a basic question.
Thank you so much in advance!!
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Employee
{
public:
Employee(string, double);
double get_salaries();
string get_name();
void set_salaries(double);
private:
string name;
double salaries;
};
Employee::Employee(string n, double s)
{
name = n;
salaries = s;
}
double Employee::get_salaries()
{
return salaries;
}
string Employee::get_name()
{
return name;
}
void Employee::set_salaries(double s)
{
salaries = s;
}
int main()
{
// using vector as an object
int i;
vector<Employee> staff(10);
staff[0] = Employee{"Harry Potter", 55000};
if (staff[i].get_salaries() < 100000)
cout << staff[i].get_salaries();
return 0;
}
Your Employee class does not have a default, parameterless, constructor.
When you create the staff vector, it will create 10 Employee objects, thus invoking the default constructor.
To support this,
vector<Employee> staff(10);
you have to provide default constructor in your class.
Your main method
int main()
{
// using vector as an object
int i; // [i] not initialized anywhere.
vector<Employee> staff(10); // Incorrect way of declaring a vector
staff[0] = Employee{"Harry Potter", 55000}; // Incorrect way of creating a instance of class
if (staff[i].get_salaries() < 100000)
cout << staff[i].get_salaries();
return 0;
}
Change your main method like this
int main()
{
vector<Employee> staff;
staff.push_back(Employee("Harry Potter", 55000));
if (staff[0].get_salaries() < 100000)
cout << staff[0].get_salaries();
return 0;
}

Initializing a dynamic memory array in a class

I am new to c++ programming, and this is probably a trivial problem, but I need to construct a variable sized array in a class and transfer text file data into it, see below. Here HISTORYFile >> ClusterCoord[i]; seems to take in the information fine, however when I try to get access to the info in the main program via,
cout << CoordClassExample.ClusterCoord[1] << "\n";
I get a bus error. Please help if you can!
class CoordClass{
public:
int Entries;
double * ClusterCoord;
void set_valuesCoord(ifstream &HISTORYFile,int MolAtomNum, int MolNum);
};
void CoordClass::set_valuesCoord(ifstream& HISTORYFile,int MolAtomNum, int MolNum) {
Entries=MolAtomNum*MolNum;
double *ClusterCoord = new double [Entries];
for (int i=0;i<Entries;i++) {
HISTORYFile.ignore(1000,'\n');
HISTORYFile >> ClusterCoord[i];
cout << ClusterCoord[i] << "\n";
HISTORYFile.ignore(1000,'\n');
}
}
You have a leak in the set_valuesCoord() function if you call the function twice, unless you somewhere release the resources. That's not the problem but it's a problem. Use a std::vector<>.
class CoordClass {
// ...
std::vector<double> ClusterCoord; // instead of double *ClusterCoord
// ...
};
What might be the problem is that you don't check whether the double parsed properly. If it didn't then you're accessing uninitialized memory, and that leads to undefined behaviour.
void CoordClass::set_valuesCoord(...)
{
// ...
double cluster_coord = 0;
if( HISTORYFile >> cluster_coord )
ClusterCoord.push_back(cluster_coord);
else
std::cerr << "Error parsing cluster coord.\n";
// ...
}
As an exercise showing the vector way that won't leak among other things:
Further changes would be to remove Entries and use ClusterCoord.size().
class CoordClass{
public:
int Entries;
std::vector<double> ClusterCoord;
void set_valuesCoord(ifstream &HISTORYFile,int MolAtomNum, int MolNum);
};
void CoordClass::set_valuesCoord(ifstream& HISTORYFile,int MolAtomNum, int MolNum) {
Entries=MolAtomNum*MolNum;
ClusterCoord.resize(Entries);
for (int i=0;i<Entries;i++) {
HISTORYFile.ignore(1000,'\n');
HISTORYFile >> ClusterCoord[i];
cout << ClusterCoord[i] << "\n";
HISTORYFile.ignore(1000,'\n');
}
}