C++ accessing vectors in classes - c++

i am a beginner in C++ and my question is:
why my vector in a class is empty when i try to access that vector elements in another class after i added elements to that vector?
i have a class for example class1 and this class has a vector of type string and a member function which adds elements to the vector with push_back() and another member function which has an argument of type string and it returns true if the argument is in the vector or else it returns false. now if i write another class class2 and it has a vector of type string named valid and a member function named check that it reads a string from input and we have a class1 object that we can access the class1 member function to check if this input is in the vector from class1 but looks like in class2 the vector i had in class1 with elements is empty. what am i doing wrong?
here is code:
class abc{
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for(string b : words) {
if(b == a) {
return true;
}
}
return false;
}
};
class b{
private:
vector<string> valid;
public:
void check() {
abc mlj;
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while(w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check();
}
screenshot of the output
i was expecting that i can access vector elements in class from another class

mlj is a new local object in the check method, and it contains no words. All your words were input in the main function and are stored in vkk. So you need to pass that object to check.
To do that, modify the method to receive a reference
void check(const abc & mlj)
{
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
// ...
valid.push_back(k);
}
Now, this will give you a compiler error, because abc::word_check is a non-const method. Let's also fix that by adding the const specifier to the method definition. While we're at it, let's accept the string as a const reference too, and also use references while iterating over the vector. This avoids unnecessary string copying.
bool word_check(const string& a) const
{
for(const string& b : words) {
if(b == a) {
return true;
}
}
return false;
}
It should be noted that this can also be achieved with std::find which is provided by the standard library in <algorithm>:
bool word_check(const string& a) const
{
return std::find(words.begin(), words.end(), a) != words.end();
}
Let's circle back to your main, and update that to call check correctly:
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk); // <-- pass vkk here
}
One other thing to note is your loop in check is broken. If w is false, then the loop will never terminate because you never update w again. How about instead you do this:
while ((cin >> k) && !mlj.word_check(k))
{
cerr << "invalid input, try again: ";
cin.ignore(INT_MAX, '\n');
cin >> k;
}
if (cin)
{
valid.push_back(k);
}
This does a couple of things at once...
First, it ensures the stream has actually read a string and not entered some error state (such as end of stream). Under ordinary conditions, reading strings from standard input will not result in error bits being set, so you also don't need cin.clear().
Second, it calls word_check every time around the loop, and only enters the loop body if the check fails. After the loop, we test once again that the stream is good, and if so then it means we read a word and it passed the check.
Make these changes, and you're at least on the way to having a working program. There are other nit-picks I could make, but I may have done too many already so I'll stop! Happy coding!

In the code that you have given as an example, you have created two separate objects of the same class, each of which occupies a different space in memory and is completely independent of each other. Therefore, the mlj object is completely independent from the vkk object and nothing has been inserted in it so far. For your code to work properly I suggest you make the following change to it. That is, give the class abc to the input of class b:
class abc {
private:
vector<string> words;
public:
void seta() {
string s;
cout << "word: ";
cin >> s;
words.push_back(s);
}
bool word_check(string a) {
for (string b : words) {
if (b == a) {
return true;
}
}
return false;
}
};
class b {
private:
vector<string> valid;
public:
void check(abc mlj) {
string k;
cout << "Enter word to check: ";
cin >> k;
bool w = mlj.word_check(k);
while (w == false) {
cerr << "invalid input, try again: ";
cin.clear();
cin.ignore(INT_MAX, '\n');
cin >> k;
}
valid.push_back(k);
}
};
int main() {
abc vkk;
vkk.seta();
vkk.seta();
vkk.seta();
b pla;
pla.check(vkk);
}

Related

How to check if a specific integer value input from keyboard exist in a line or more lines of a file in C++

I have a small project for a C++ course and I'm stuck trying to check if a value of a data member of STUDENT's class exists in the file(the "ID"). I've tried to use some function that I found on the internet to transform the integer value I'm searching for into a string and then use the find function, to search for it in each line of the file.
It works, but whenever I check one line from the file, it gets false pozitive, because the ID value(for example "12") is for example, identical to the value of age(also "12"). It does that because the age value comes before the ID value in my file and also in the string variable (and I can't change it). I don't know to search in the string for the value of ID only. I use the function "inputInfo" to input student1's member values from the keyboard, and function "checkID" to check if value of "ID" already exists in the file. Also, for another aspect of the project, I am seeking a way to search for occurrence of the ID and name data members values in the same file(once they are already written). One solution I've thought is to somehow start the search after the occurence of another character(for example the space character, given the fact that in the file, each field is delimited from another with a space), but I'm not sure the find function is able to do that.Thank you in advance for your help.Below is a part of the project's code in C++:
#include<iostream>
#include<string>
#include<fstream>
#include <sstream>
using namespace std;
int checkID(int idNumber)
{
string findID;
stringstream id_string;
id_string << idNumber;
findID = id_string.str();
int offset;
ifstream in;
in.open("Students.txt");
if(in.is_open())
{
string line;
while(getline(in, line))
{
if(offset = line.find(findID, 0)!= string::npos)
{
cout<<"The ID already exists. Insert a different ID!"<<endl;
return 0;
}
}
}
else
cout<<"File doesn't exist!"<<endl;
in.close();
}
class PERSON
{
protected:
string name;
string surname;
unsigned int age;
public:
void inputinfo()
{
cin>>name;
cin>>surname;
cin>>age;
}
outputinfo()
{
cout<<name<<endl;
cout<<surname<<endl;
cout<<age<<endl;
}
};
class STUDENT: public PERSON
{
int ID;
float marks_sum;
string belonging_class;
public:
inputInfo()
{
cout<<"Name:";
cin>>name;
cout<<"Surname:";
cin>>surname;
cout<<"Age:";
cin>>age;
do
{
cout<<"ID:";
cin>>ID;
}
while (checkID(ID)==0);
cout<<"Sum of marks:";
cin>>marks_sum;
cout<<"The belonging class:";
cin>>belonging_class;
}
void outputInfo()
{
cout<<name<<endl;
cout<<surname<<endl;
cout<<age<<endl;
cout<<ID<<endl;
cout<<marks_sum<<endl;
cout<<belonging_class<<endl;
}
friend std::ostream& operator << (std::ostream& os, const STUDENT& value )
{
os << value.name<<" "<<value.surname<<" "<<value.age<<" "<<value.ID<<" "<<value.marks_sum<<" "<<value.belonging_class<<std::endl;
return os;
}
};
STUDENT student1;
int writeInFile(STUDENT studentx)
{
ofstream os("Students.txt", ofstream::app);
os << studentx;
os.close();
}
int main()
{
int opt1, opt2;
char option;
do
{
cout<<"1 - Input data into file"<<endl<<"2 - Close program"<<endl;
cin>>opt1;
switch(opt1)
{
case 1:
do
{
cout<<endl;
cout<<"Choose one of variants"<<endl<<"1.Students"<<endl<<"2.Get back to main menu"<<endl;
cin>>opt2;
switch(opt2)
{
case 1:
do
{
cout<<"Do you wish to introduce a new student(Y/N)?";
cin>>option;
if(option!='N')
{
student1.inputInfo();
writeInFile(student1);
}
}
while (option!='N');
break;
}
}
while(opt2!=2);
break;
}
}
while(opt1!=2);
}
#include <sstream>
using namespace std;
bool isUniqueID(ifstream& file, int id)
{
string id_string = to_string(id);
string currently_read_line;
// The position of the searched key. So, in this case,
// only the 3rd value will be tested (starting from 0).
// John Doe 23 456
// | | | |
// 0 1 2 3 (the id)
int offset = 3;
while (getline(file, currently_read_line))
{
istringstream ss(currently_read_line);
string current_entry;
int counter = 0;
while (ss >> current_entry) {
if (current_entry == id_string && counter == offset) {
cout << "The Id already exists." << endl;
return false;
}
counter++;
}
}
// No match found
cout << "The ID does not exist yet." << endl;
return true;
}
Please note:
Just pass your opened file to the function. The file is opened once, instead of opening it every time you want to check an ID.
This requires to compile in -std=c++11 (for the to_string conversion)
[Update]
The offset variable tells the function what value to test for. A more consistent way to do this, would be to format the data as to have a key/value for each student entry. It works as it though.

Passing a variable from main into a public class function?

I was wondering how in this situation I can pass a variable from main into a public class function. In this situation the health variable doesn't change at all even though it should. Here's my code:
class PlayerCharacter
{
public:
void SetHealth(int Health)
{
m_health = Health;
}
int GetHealth()
{
return m_health;
}
private:
int m_health;
int m_maxhealth;
};
int main()
{
PlayerCharacter PC;
bool playing = true;
int Choice;
int v1;
int v2;
while (playing)
{
PrintMainMenu();
cout << "Set Health And Max Health" << endl;
cin >> Choice;
v1 = Choice;
cin >> Choice;
v2 = Choice;
PC.SetHealth(v1);
PC.SetMaxHealth(v2);
system("CLS");
}
return 0;
}
Is there something I'm missing here? Thanks.
edit: All of my code
From your code link, your PrintMainMenu() function is creating an entirely new Character each time. It has no relation to the one being edited in main().
You should change it to accept a Character as a reference and use that to print your stats:
void PrintMainMenu(Character& PC) {
...
}
You can try using getline (cin, input) instead of cin>> as reading directly with cin is not type safe and as far as I know, it does not remove newline character. Also, it does not perform length check. So it has been some time not to use C++ but if I remember correctly getline works better.
getline (cin, Choice);
In function void PrintMainMenu() you are creating a new Character.
You need to pass a reference on your Character from main into this function.

Find an item in vector created by own StringTokenizer

I have made my own StringTokenizer class:
class StringTokenizer {
private:
vector<string> tokens; //output
string strIn; //input
public:
StringTokenizer(string strIn) {
this->strIn = strIn;
string str(strIn);
string tmpstr;
stringstream ss(str);
while(ss >> tmpstr)
tokens.push_back(tmpstr);
}
vector<string> getTokens() {
return tokens;
}
}
I have my main function
int main() {
string str = "a b c d";
StringTokenizer st(str);
if(find(st.getTokens().begin(), st.getTokens().end(), item) != st.getTokens().end()) {
cout << "found";
} else {
cout << "not found";
}
}
When function is returning true, everything is working fine. Otherwise program is crashing. What am I doing wrong?
You have undefined behaviour in your code, getTokens() returns a vector by value and you call that twice (once for begin(), once for end()) which means your two iterators don't point to the same container. This just cannot work. And then, you call it a third time to compare the result of find which is also incorrect for the same reason.
You can easily fix this by changing it to be:
auto tokens = st.getTokens();
if(find(tokens.begin(), tokens.end(), item) != tokens.end())
or you can return const vector<string>& from getTokens().

User Inputs Value, but Arbitrary Value Printed to Screen. Printed Value doesn't Match Entered Value

#include<iostream>
#include<string>
using namespace std;
//class definition
class BankCustomer
{
public:
BankCustomer(); //constructor for BankCust class
void fullname(string, string);
string firstname();
string lastname();
bool setsocial(int s); //accept a arg. of int type
int getsocial();
private:
string fname, lname;
int SSNlength; //can't be changed by client; sensitive info should be made private
};
//class implementation
BankCustomer::BankCustomer(){}
void BankCustomer::fullname(string f, string l)
{
fname=f;
lname=l;
}
string BankCustomer::firstname()
{
return fname;
}
string BankCustomer::lastname()
{
return lname;
}
bool BankCustomer::setsocial(int s)
{
int count, SSNlength;
while(s != 0)
{
s /=10; //counts number of integers; count goes to max of ten
++count;
if(count == 9)
{
cout <<"\nValid SSN Entered!" << endl;
SSNlength=s;
return true;
}
}
}
int BankCustomer::getsocial()
{
return SSNlength;
}
//client program
int main()
{
BankCustomer customer; //customer declared as object of BankCust class
string firstname, lastname;
int ssn, s;
//data assignment
cout <<"\n Enter First Name\n" << endl;
cin >> firstname;
cout<<"\n Enter Last Name\n"<< endl;
cin >> lastname;
customer.fullname(firstname,lastname);
do
{
cout<<"\nEnter 9-Digit SSN"<< endl;
cin >> ssn;
}
while(!customer.setsocial(ssn)); //function will repeat as long as entered user ssn forces social() to evaluate it as false
//data ouput
cout <<"\nFirst Name: "<<customer.firstname()<<"\n"<< endl;
cout <<"\nLast Name: "<<customer.lastname()<<"\n"<< endl;
cout <<"\n SSN is: "<<customer.getsocial()<<"\n" << endl; //not printing correct value
}
When I run the program, entered user input of first and last name is printed correctly to screen. However, when I try to print entered value of SSN, the program gives back a garbage value that doesn't match the one user entered. Issue occurs when return value of customer.getsocial() is printed on cout<<"\n SSN is: line.
Your member variable SSNlength is unintialized, while you define a local variable with the same name in setsocial(int s) at
int count, SSNlength;
Thus, your member variable will not be initialized as your local variable hides it, which means that getsocial() will always return garbage ...
Also, you should return false from setsocial(int s) if your input s is not valid to avoid undefined behaviour. May be something like
bool BankCustomer::setsocial(int s)
{
SSNlength = s;
int count;
while(s != 0)
{
s /=10;
++count;
if(count == 9)
{
cout <<"\nValid SSN Entered!" << endl;
return true;
}
}
return false;
}

Arithmetic from an input file to an output file?

This is a two part question and I hope I make myself understood. I'll edit as necessary! I'm attempting to write a program that will do calculations in succession from an input file. The text file will look something like this:
int + 25 10
double / 5.5 8.5
...
Each instance starts with a type like int, double, float, etc., then a calculation type (add, subtract, etc.) and then two numbers. I want to be able to read each in succession and output the sum, product, etc. into an output file. For instance, if we use the first example above, the output in the file will be:
int 25 10 = 35
I have code which will do the calculations below:
void doAddition(ifstream &inFile) {
int num1, num2;
inFile >> num1 >> num2;
cout << num1 << num2 << " = "<< (num1+num2) << '\n'; }
The only problem with this is that I don't know how to add the type of variable (I've tried using string but it doesn't seem to work), for instance "int" or "double" so I get:
25 10 = 35
Instead of:
int 25 10 = 35
My second problem as you may see is that I'm currently using "cout" to show the information on screen when I really want to add it to the outfile. Here's some more info:
What I use to move to the next line:
void readToNextLine(ifstream& inFile) {
string t1, t2;
inFile >> t1 >> t2; }
Code in my Main:
ifstream inFile;
//ofstream outFile;
char ch;
int num1, num2;
inFile.open("infile.txt");
//outFile.open("outfile.txt");
if (inFile.is_open()){
inFile >> ch;
while (!inFile.eof())
{
switch (ch)
{
case '+':
doAddition(inFile);
break;
...
As you can see I commented out the ofstream part because I couldn't get it to work properly. Any suggestions? I have about 10 windows and two C++ books open right now just trying to put it all together logically!
Edit: I'm not sure if switches would be the best method for this. I need the program to see "int" and realize it's a word. If I used 4 variable types like int, double, float, and long perhaps I could have it check the first letter of each: i, d, f, l and then once it knows what type it could go into the +, -, etc. checks. It feels like by doing this logically it's just taking more time when I could be using a series of classes, but I'm just not sure where to start with that.
I'm really not understanding all this trouble reading from files. There are too many examples on Stackoverflow and the web. Perhaps it's people not searching or they are demanding an example that matches their exact code.
Try this:
struct Input_Record
{
std::string data_type_as_string;
std::string operation;
std::string value1_as_string;
std::string value2_as_string;
friend std::istream& operator>>(std::istream& inp, Input_Record& r);
};
std::istream& operator>>(std::istream& inp, Input_Record& r)
{
inp >> r.data_type_as_string;
inp >> r.operation;
inp >> r.value1_as_string;
std::getline(inp, r.value2_as_string); // Use getline to eat the line ending.
}
// ...
Input_Record r;
while (input_file >> r)
{
// Do stuff with r
};
If you really want to have some fun, You could use a parent base class and a factory pattern to generically create objects based on the input:
class Binary_Operation // Base class for factory pattern.
{
public:
//! Evaluate the object and return the result as a string
// e.g. numbers as text
virtual std::string evaluate(void) const = 0;
};
class Binary_Integer_Operation : public Binary_Operation
{
public:
std::string evaluate(void) const
{
// Convert values to integers than perform the operation.
// Convert result to string using std::istringstream.
};
};
class Binary_Double_Operation : public Binary_Operation
{
// See Binary_Integer_Operation above.
};
This allows you to do something like:
Binary_Operation * factory_create(const Input_Record& r)
{
Binary_Operation * p_obj = nullptr;
if (r.data_type == "int")
{
p_obj = new Binary_Integer_Operation;
// Initialize fields
}
if (r.data_type == "double")
{
p_obj = new Binary_Double_Operation;
// Initialize fields;
}
return p_obj;
}
Your processing loop would look like:
Input_Record r;
while (input_file >> r)
{
Binary_Operation * p_operation = factory_create(r);
std::string result = p_operation->evaluate();
cout << "result = " << result << "\n";
delete p_operation;
}
Let's start with an example like the one you provided:
int + 25 10
The type for the "type" and arithmetic operator is simple, std::string and char respectively.
std::ifstream in("infile.txt");
std::string type; char op;
if (in >> type >> op)
{
// ...
}
For the other two values, you also have to extract them into strings because you first have to find out the value of type before you can convert them:
if (in >> type >> op >> a >> b) // a and b are strings
Now use a function to check type and convert a and b to the correct types:
void convertTo(std::string const& typeName, std::string const& a, std::string const& b, char op)
{
if (typeName == "int")
{
int a1 = std::stoi(a),
b2 = std::stoi(b);
doOperation(op, a1, b2)
} else if (typeName == "double") {
double a1 = std::stod(a),
b2 = std::stod(b);
doOperation(op, a1, b2);
} else if (typeName == "float") {
// std::stof()
}
}
doOperation() is templated and is implemented like this:
template<typename T>
struct F;
template<> struct F<int> { static const std::string value = "int"; };
template<> struct F<double> { static const std::string value = "double"; };
template<> struct F<float> { static const std::string value = "float"; };
template<typename U, std::string name = F<U>::value>
void doOperation(char op, U a, U b)
{
std::ofstream out("outfile.txt");
switch (op)
{
case '+':
out << name << " " << op << " " << (a + b);
case '-':
out << name << " " << op << " " << (a - b);
case '/':
// ...
// ...
}
}