table of strings array - c++

#include <iostream>
#include "HtmlTable.h"
using namespace std;
int main()
{
cout << "Content-type: text/html" << endl << endl;
HtmlTable t(2,3);
t.insert(2,1, "one");
t.insert(1,2, "two");
t.insert(2,3, "three");
t.print();
return 0;
}
#ifndef HTMLTABLE_H
#define HTMLTABLE_H
#include <string>
#include <iostream>
using namespace std;
class HtmlTable
{
public:
HtmlTable(int y, int x)
{
}
void print()
{
cout << "<table>";
for (row=0; row<y; row++)
{
cout << "<tr>";
for (col=0; col<x; col++)
{
cout << "<table border='1'>";
cout << m_Table[y][x];
}
cout << "</td>";
}
cout << "</table>";
}
void insert(int row, int col, string text)
{
y = row;
x = col;
z = text;
m_Table[y][x] = {{z,z,z,z},{z,z,z,z},{z,z,z,z},{z,z,z,z}};
}
protected:
private:
string m_Table[100][100];
int row;
int col;
string text;
int x;
int y;
string z;
int get_x = x;
int get_y = x;
};
#endif // HTMLTABLE_H
I have to create a 2d array of strings.
There is an insert function that inserts a string into a certain spot in the array.
Then the print function should print a table with the words inside the corresponding box.
output should be something like this:
|____| two |______|
| one|_____| three|
I am given int main and cant change anything.
My current issue is with the void insert. the error is:
no match for 'operator=' in
'((HtmlTable*)this)->HtmlTable::m_Table[((HtmlTable*)this)->HtmlTable::y]
my past attemps only printed the last spring and repeated in for every box in the table.
what am i doing the array incorrectly? is my print function also incorrect?

This line:
m_Table[y][x] = {{z,z,z,z},{z,z,z,z},{z,z,z,z},{z,z,z,z}};
is plain wrong. m_Table is a 2D array or strings, so m_Table[y][x] is just a std::string. You should write: m_Table[y][x] = z.
But there are many other problems in your code:
you pass the array dimensions in HtmlTable constructor but ignore them
you declare z, row, col, text as member variable when they does not store state: they should be local variable of member functions
these two lines
int get_x = x;
int get_y = x;
declare unused member variable and try to initialize them which is incorrect. Member variables should be initialized in constructor (except for integral static const ones)
print make row loop from 0 to y, and col from 0 to x (which is right if x and y are initialized), but consistently writes m_Table[y][x] instead of m_Table[row][col]
And your print method is wrong... because you never initialized x and y. Your constructor should be:
HtmlTable(int y, int x): x(x), y(y)
{
}
and you should not modify them in insert:
void insert(int row, int col, string text)
{
m_Table[row][col] = text;
}
Array are 0 indexed in C++. Your main should contain:
t.insert(1,0, "one");
t.insert(0,1, "two");
t.insert(1,2, "three");
BTW, generated HTML is incorrect: you do not close <tr> tag, not open <td> one, and nest <table> tags in a strange way but this would be another story...

Related

Store dynamic amounts of data in a class C++

I have a class that stores data and within the class I have an array called 'position' that stores strings of 2 characters. Unfortunately, the amount of 2 character strings it should hold will vary:
class shipStatus
{
public:
char* name;
int x{};
char position[x][2]; // does not work
void setInfo(char name[], int x);
void displayStatus();
};
The setInfo function assigns a numerical value to x, which varies among objects. I would like the value of x to also dictate the length of the character array 'position'.
For example:
if x = 3 then
char position[3][2]; // the length of the second array is always 2
How can I make my code do this? If I try adding a variable as the parameters my code does not compile.
Here is my setInfo function:
void shipStatus::setInfo(char name[], int x)
{
name = name;
x = x;
}
Since this is C++, you should use the C++ facilities that are available to you. In your case, it would be std::string, std::vector, and std::array.
Below is an example using basically what your original shipStatus structure consisted of, and changing it to using the above mentioned constructs:
#include <string>
#include <array>
#include <vector>
#include <iostream>
class shipStatus
{
std::string name; // <-- Replaced char*
std::vector<std::array<char, 2>> position; // <-- A vector of an array that has a size of 2
public:
void setInfo(std::string n, int x);
void displayStatus();
void setPosition(size_t whichItem, char c1, char c2);
size_t getNumPositions() const;
};
void shipStatus::setInfo(std::string n, int x)
{
name = n;
position.resize(x); // <-- All we need to do is resize() to dynamically resize the vector
}
void shipStatus::displayStatus()
{
for (auto& p : position)
std::cout << p[0] << " " << p[1] << "\n";
}
void shipStatus::setPosition(size_t whichItem, char c1, char c2)
{
position[whichItem] = {c1, c2}; // <-- we set one item in the vector, and in
// that item, we set the [0] and [1] characters
}
size_t shipStatus::getNumPositions() const { return position.size(); }
int main()
{
shipStatus sStatus;
sStatus.setInfo("Ship 1", 3);
sStatus.setPosition(0, '4', '2');
sStatus.setPosition(1, 'a', 'b');
sStatus.setPosition(2, 'y', 'z');
std::cout << "Number of positions: " << sStatus.getNumPositions() << "\n";
sStatus.displayStatus();
}
Output:
Number of positions: 3
4 2
a b
y z
Note that we no longer need x as a member, since a std::vector knows its size already by calling the size() member function.
char **position;
position = (char **)malloc(sizeof(char *)*x);
for (int i=0; i<x; ++i)
{
position[i] = (char *)malloc(sizeof(char)*2);
}
This is a classic "C" way of doing it, will work in C++ too.
For a cleaner approach, we should use vectors/lists for this purpose.

Calling name for passing two arrays (a string and an int) to a function in C++

I'm aware that when you write the call for your function you write it as displayArray(seasons,10)
with the name of one array and its size. I'm stuck on how you would right the arguments to pass the two arrays listed in my code, seasons and cartoons.
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
void displayArray(string car[], int sea[], int size);
int main()
{
int seasons[] = {5,10,8,2,12,7,31,9,3,4};
string cartoon[] = { "Steven Universe","Adventure Time","Regular Show","Gravity Falls",
"Spongebob Squarepants","Futurama","The Simpsons","Bob's Burgers","Avatar: The Last Airbender","Rick and Morty"};
displayArray() // Error Message here
}
void displayArray(string car[], int sea[], int size)
{
for (int x = 0; x < size; x++)
{
cout << " " << car[x] << "\t\t" << sea[x] << right << endl;
}
}
So you have to first create an array to pass your values with. Then just pass the array.
void function(int arr[]) {}
int arr[] = { 1, 2, 3, 4, 5 };
function(arr);
So in your code above, it should look like this:
int main()
{
int seasons[] = {5,10,8,2,12,7,31,9,3,4};
string cartoon[] = { "Steven Universe","Adventure Time","Regular Show","Gravity Falls",
"Spongebob Squarepants","Futurama","The Simpsons","Bob's Burgers","Avatar: The Last Airbender","Rick and Morty"};
displayArray(cartoon, seasons, 10);
}
Hope this helps :)
displayArray(cartoon, seasons, 5);
This seems to work fine for me. You just pass each array in according to whichever is declared first in the function argument list. Am I misunderstanding your question?

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.

Trouble while writing an resiseable vector with std::vector

i'm a Brazilian beginner in c++ coding (apologize my poor knowledge in both things). I'm trying to write an .txt output file containing the positions of pixels i click with the mouse. I'm making use of opencv library, so thats a functional part of the code:
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <vector>
#include <fstream>
using namespace std;
using namespace cv;
//declaration of vector and counter
int i = 1;
std::vector<int>vet_x(i);
std::vector<int>vet_y(i);
//the callback function
void CallBackFunc(int event, int x, int y, int flags, void* userdata)
{
if (event == EVENT_LBUTTONDOWN)
{
vet_x.resize(i);
vet_y.resize(i);
vet_x[i] = x;
vet_y[i] = y;
i++;
cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
}
}
int main(int argc, char** argv)
{
Mat img = imread("lena.jpg");
//Create a window
namedWindow("Mouse Track Test", 1);
//set the callback function for mouse event
setMouseCallback("Mouse Track Test", CallBackFunc, NULL);
//show the image
imshow("Mouse Track Test", img);
// Wait until user press some key
waitKey(0);
//the writing begins after the press of the key
ofstream myfile;
myfile.open("points.txt");
for (int j = 1; j <= vet_x.size(); j++)
{
cout << vet_x[j] << "," << vet_y[j] << endl;
myfile << vet_x[j] << "," << vet_y[j] << endl;
}
myfile.close();
return 0;
}
The problem is: the file write only the last clicked position!
But if i turn the "vet_x.reserve(1024);" line, it works well, but only for the y coordinates...
So, what is my mistake?
C++ array indexing is 0-based. Thus when you resize a vector v to size 1, and assign to v[1], you're assigning to a non-existing item. This is Undefined Behavior.
To catch this kind of out-of-bounds indexing you can use the at method, which guarantees an exception. I.e., writing v.at(i) instead of v[i].
However, you should simply use the push_back member function to add items to a vector. I.e., v.push_back( x ), where x is the value that you want to add. It can also be a good idea to use a single vector of 2D points, instead of one vector for x and one vector for y.
vet_x.resize(i);
vet_y.resize(i);
vet_x[i]=x;
vet_y[i]=y;
You are assigning elements out of range, which is an undefined behavior. After resize(i) the last valid index is i-1. std::vector operator [] never insert elements in the container.
Rather just do
vet_x.push_back(x);
vet_y.push_back(y);
Your method of adding variables into vectors is wrong. I suggest this:
struct Point
{
int x, y;
Point(int sx, int sy)
:x(sx),y(sy)
{
}
};
std::vector<Point> clickedPositions;
//the callback function
void CallBackFunc(int event, int x, int y, int flags, void* userdata)
{
if ( event == EVENT_LBUTTONDOWN )
{
clickedPositions.push_back(Point(x,y));
}
}
and while writing it into the file:
for(int j=0; j<clickedPositions.size(); j++)
{
myfile << clickedPositions[j].x < <","<< clickedPositions[j].y <<endl;
}

C++ Member Variable value changes based on whether or not it is printed out

I have a class ZoneDeVie containing a vector of vectors of Bacterie*. The Bacterie class contains an int value energie (set to 10 by default) and a toString() function which prints the value. In the ZoneDeVie constructor, I build the 2D table, populating each cell with a default instance of a Bacterie. Then, in my main method, I'm testing by printing the toString() of the last Bacterie in the table. For some reason, it returns a random, obnoxiously-large int (usually something like: 3753512); however, if I make a call to the Bacterie's toString() method in the constructor of ZoneDeVie, the main method will print out correctly.
#include <iostream>
#include <sstream>
#include <vector>
using namespace std;
class Bacterie {
public:
Bacterie() { this->energie = 10; }
string toString() {
stringstream ss;
ss << "Energie: " << this->energie;
return ss.str();
}
protected:
int energie;
};
class ZoneDeVie {
public:
ZoneDeVie(int width, int height) {
Bacterie* bac = new Bacterie();
// without this [following] line, the call to `toString`
// in the main method will return an obnoxiously-large value
//bac->toString();
for (int i=0; i<height; i++) {
vector<Bacterie*> bacvec = vector<Bacterie*>();
this->tableau.push_back(bacvec);
for (int j=0; j<width; j++) {
this->tableau[i].push_back(bac);
}
}
}
vector<vector<Bacterie*> > tableau;
};
int main(int argc, char *argv[]) {
int x,y;
x = 9; y = 39;
ZoneDeVie zdv = ZoneDeVie(10,40);
cout << "zdv(" << x << "," << y << ") = " << zdv.tableau[x][y]->toString();
return 0;
}
output (with a call to "toString()" in ZoneDeVie's constructor): zdv(9,39) = Energie: 10
output (w/o a call to "toString()" in ZoneDeVie's constructor): zdv(9,39) = Energie: 4990504
Why in the world do I need to call my toString() method before calling it in the main method in order for it to behave as expected?
The end condition in your for loops are swapped. You should first iterate through width and then through height:
class ZoneDeVie {
public:
ZoneDeVie(int width, int height) {
Bacterie* bac = new Bacterie();
for (int i=0; i<width; i++) {
vector<Bacterie*> bacvec = vector<Bacterie*>();
this->tableau.push_back(bacvec);
for (int j=0; j<height; j++) {
this->tableau[i].push_back(bac);
}
}
}
vector<vector<Bacterie*> > tableau;
};
This compiles and provides the correct output.
There are several issues with this code.
It's not clear what the default constructor of Bacterie does.
It's not clear what ZoneDeVie::tableau is and how the local vector bacvec is used.
It's not clear how the copy constructor and operator= for class ZoneDeVie are defined (both are used in main()).
It seems that all entries in your table are initialised with a pointer to the same Bacterie bac