How can you initialize an object from a variables value? - c++

I would like to generate the name of a soon to be initialized object by getting the user input. Is this possible? Is there a way to name an object based on a variables value?
I know in the code below the "test x" doesn't work and I know why... but is there a way to get this to work? I have been trying to figure it out for 2 days now. The closest thing I came to was maybe stringstreams... or even worse... it isn't possible? Any help?
#include <iostream>
using namespace std;
#include <string>
class test
{
public:
string wtf;
};
int main()
{
test y; // showing normal initialize
y.wtf = "a"; // assign value
string x;
cin >> x; // initialize string and get input
test x // trying to initialize object based on input
x.wtf = "a"; // then assign a value
cout << y.wtf; // printing both to test
cout << x.wtf;
return 0;
}
My intent is to have a single array that holds employee numbers (concatenated with a an "emp" or something in the beginning) to initiate objects for each employee. So the user would input an employee number, say 1234, and I would make a string that added emp + 1234 and come out with "emp1234"... which I would then initiate an object like "test emp1234" which would have a bunch of difference variables associated with it, inside the class.
I may be looking at this all wrong... and probably explained it fairly crappy. I am obviously new to this, but I could use any help possible.

I would do it like this:
#include <vector>
#include <string>
#include <iostream>
#include <stdexcept>
test make_test()
{
std::string s;
if (!(std::cin >> s))
{
throw std::runtime_error("Premature end of input");
}
return { "emp" + s };
}
int main()
{
std::vector<test> v;
for (int i = 0; i != 10; ++i)
{
v.push_back(make_test());
std::cout << "Added employee '" << v.back().wtf << "'\n";
}
}
This would fill a vector with ten employee records.

No, you can't create variable names at run-time simply because names do not exist at run-time. They are static constructs that are only useful during compilation.
However, it really sounds like you just want a std::map. You can use this to map from integers (employee number) to an test object:
std::map<int, test> employees;
int employeeNumber;
std::cin >> employeeNumber;
employees[employeeNumber]; // Will default construct a test object mapped to employeeNumber
// Alternatively:
test t( /* some constructor args */ );
employees[employeeNumber] = t;
Then, if you want to access the test object with employee number 1234, for example, you could simply do:
employees[1234]

Related

Writing a vector of objects to file and then reading it

I'm new to C++ and stackoverflow so forgive me any mistakes in my post ;). I need to create a code, which allows me to fill new objects with data from std::cin and export these objects to binary file later. Also, I need to import objects exported to file at some point. Objects represent users with standard user information like username, ID, lvl etc.
#include <vector>
#include <string>
#include <iostream>
class User {
std::string username;
unsigned int ID, lvl;
public:
User(std::string un, int uID, int ulvl) {
username = un;
ID = uID;
lvl = ulvl;
}
};
int main() {
std::string u_name;
int u_ID,u_lvl;
bool finish = false;
char choice;
std::vector<User> test_user_vec;
do {
std::cout << "Enter username: ";
std::cin >> u_name;
std::cout << "Enter ID: ";
std::cin >> u_ID;
std::cout << "Enter lvl: ";
std::cin >> u_lvl;
test_user_vec.push_back(User(u_name, u_ID, u_lvl));
std::cout << "Do you want to add another user? (y/n)?";
choice = getch();
if (choice == 'y') finish = true;
} while (!finish);
return 0;
}
I assume that test_user_vec stores every object I created while my program is running. My problem occurs when I want to export that vector to file. The purpose of this action is to store objects' data even after my program terminates and import all the data when I run my program again.
I was trying to solve this problem on my own, nothing really came to my mind. While I was looking for some info i found something like this:
#include <fstream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> v{ "one", "two", "three" };
std::ofstream outFile("my_file.txt");
// the important part
for (const auto &e : v) outFile << e << "\n";
}
I've tested it with <string> and <int> vectors and my variables. It's good until I try to export <object>vector.
Also i found another solution and tried to do something with it on another test code:
class Test {
public:
int number;
float number2;
};
int main(){
Test test1;
test1.number = 122;
test1.number2=12;
std::fstream testfile("test1.bin", std::ios::out | std::ios::binary);
testfile.write((char*)&test1, sizeof(test1));
testfile.close();
//ater writing an object with variables i commented this section
//then uncommented this section and run the program again
std::fstream testfile2("test1.bin", std::ios::in);
testfile2.read((char*)&test1, sizeof(test1));
std::cout << test1.number;
testfile2.close();
return 0;
}
Again, it works, i can read test1.number until I want to use vector of objects, not a single object. With vector of objects my cout printed some random values like 11314123e-03.
I was trying to somehow combine these 2 solutions, but nothing worked out. I would like to have a binary file, because i heard it's faster and has any data protection (i can't just open it in notepad and read the data) I'm new to c++, there is a great chance of me trying to do it reeeeeealy inefficient way, so pls help :D.
Data member getter functions can be added to the User class and used in fstream output operations. This should provide the general idea:
std::string userName;
for (const auto &u : v)
{
outFile.write(u.GetID(), sizeof(int));
outFile.write(u.GetLvl(), sizeof(int));
userName = u.GetName();
outFile.write(username.length(), sizeof(size_t));
outFile.write(userName.data(), username.length());
}
For userName, the length is written to precede the userName string data in the file so that the file can be parsed when read. The binary encoding/convention is designer's decision as there are several options. Another option would be to encode the entire object as a null-terminated string, although this would generally be less size efficient except for the userName string itself.
Note: test_user_vec.push_back(User(u_name, u_ID, u_lvl)); is creating temporary User objects on the stack. As #drescherjm and #RaymondChen pointed out, that is OK, but this is a better alternative: test_user_vec.emplace_back(...);

C++ map empty after inserting data.

Firstly, Happy new year to those who come across my question.
I'm currently learning C++ and I have a class project to complete.
In a nutshell, my code (so far) is supposed to instantiate student objects with details read from a txt file (name, reg-number, and a map of ) and add the student to a list.
I then read a second txt file (consisting of reg-number, course code and mark) and check if there is a match of student reg numbers between whats read and the list.
if there is a match, I should insert the marks read from the txt file into the map (part of the student object), such that afterwards, each student has a map containing the courses taken and marks achieved
I seem to be creating the list fine, then I use a stringstream to read the second file and loop through the list to compare reg-numbers.
if there is a match I then call the add mark method to add marks to the map.
Here's the thing.. If after I completed the map inserting, I loop and print a map of a student, the map is empty. for all students. To confirm this I used map.size().
I have tried many ways to understand and rectify the issue but it seems i'm missing the point of something. Instinct tells me that the add mark method is copying a reference to the variable passed, which is then destroyed by the stringstream in the main method, thus showing no data in the map. unfortunately, I can't change any code within the header files, only implement what's declared.
after reading the std library docs for strings, maps etc, and attempting numerous ways of correcting the behaviour, I'm at a loss.
Any advice would be greatly appreciated so I can continue with the project and better understand what is happening. I have added the files below. there is also a Person base class but I havnt changed this as it consists of only a setter and getter.
Many thanks in advance.
student Header:
#ifndef _STUDENT_H_
#define _STUDENT_H_
#include <string>
#include <map>
#include <stdexcept>
#include "Person.h"
using namespace std;
class NoMarkException: public exception
{
};
class Student: public Person
{ public:
// constructor should initialise name and registration number using arguments
// and initialise marks map to be empty
Student(const string &name, int regNo);
// method to return registration number
int getRegNo() const;
// method to add the mark to the map
// if a mark for the module is already present it should be overwritten
void addMark(const string& module, float mark);
// method to retrieve the mark for a module
// should throw NoMarkException if student has no mark for that module
float getMark(const string &module) const throw (NoMarkException);
private:
int regNo;
map<string, float> marks; // keys are modules, values are marks in range 0.0 to 100.0
// friend function to output details of student to stream
// should output name, regno, and minimum, maximum and average marks on a single line
// if the student has no marks "has no marks" should be output instead of the marks
friend ostream& operator<<(ostream &str, const Student &s);
};
#endif
Student.cpp file:
#include <iostream>
#include "Student.h"
#include "Person.h"
using namespace std;
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
map<string, float> marks;
}
//implemented from the header file. Returns the registration number.
int Student::getRegNo() const {
return regNo;
}
// implemented as per header file request. adds marks to the map. if a mark exists, then it is overwritten.
void Student::addMark(const string &module, float mark) {
marks[module] = mark;
cout << "added: " << marks[module]<< endl;
}
//used to find marks in a map.
float Student::getMark(const string &module) const throw (NoMarkException) {
auto search = marks.find(module);
//line to test the map size after using addMark.
cout << "size after inputted values: "<< marks.size();
return marks[module];
}
main.cpp file
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <list>
#include "Student.h"
using namespace std;
//main method to obtain a file name from a user and read the file.
//method passes each .. to student.cpp
int main() {
//setting up variables required
string fileName;
const string fileEnd = ".txt";
string line;
string line2;
ifstream file;
int reg;
int reg2;
string studName;
string lastName;
float mark;
string module;
list<Student> listOfStudents;
cout << "Please enter a file name to access..." << std::endl;
cin >> fileName;
fileName += fileEnd;
// opening file an reading its contents. values are prepared and sent to the Student constructor. Fills a list
// with student objects created with variables read from the studs.txt file.
//checks file is found and exits the program if not
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while (getline (file, line)) {
stringstream stream (line);
stream >> reg >> studName >> lastName;
studName += (' ' + lastName);
cout << "Student: " << studName << " has been created." << endl;
listOfStudents.push_front(Student(studName, reg));
}
file.close();
cout << "The list of students has been created :)" << endl << endl;
cout << "Please enter the name of the next file to open"<< endl;
cout << listOfStudents.size()<<endl;
// opening second file. If file not found, exit with an error code.
// otherwise read each line, separate the three words into variables, then loop through the previously created list
//and look for a match of regNo. if true then call the addMark method to add the module and mark to the student's map.
cin >> fileName;
fileName += fileEnd;
file.open(fileName);
if (!file) {
cerr << "Unable to open file " << fileName << endl;
exit(1);
}
while(getline(file, line))
{
istringstream line_stream(line);
line_stream >> reg2 >> module >> mark;
for(Student stud : listOfStudents){
if(stud.getRegNo() == reg2){//out << "Match reg2: " << reg2 << " with stud: " << stud.getName() <<stud.getRegNo()<< endl;
stud.addMark(module,mark); }
}
}
//testing the get mark method of student class. with a module that is in the map. expecting a result
for(Student s :listOfStudents){
cout << s.getMark("CE151") << endl;
}
return 0;
}
You create a temporary copy of Student each time you're going to addMark, and discard it afterwards. You need not to copy it, but use reference, like this:
for (auto& stud: listOfStudents) /* stud.addMark */
It would also be a good idea to avoid copying Student when you print the results:
for (const auto& stud: listOfStudents) /* stud.getMark */
Aaaand one more thing:
float getMark(const string &module) const throw (NoMarkException);
Dynamic exception specification is deprecated in C++11 and removed in later standards. It is considered to be a bad practice and should be avoided.
The answer by user: grungegurunge is pretty much the one you are looking for. However, after looking at your class. I noticed that in the private section for the member variables you have declared map<string, float> marks which is okay. Yet when I look at your class's constructor after you set the name and regNo it appears that you are declaring another map<string, float> named marks where this one is local to the constructor only and does nothing. You create local memory on the stack with automatic storage, never use it, then it gets destroyed after the object goes out of scope; it is unnecessary to have it declared here.
//constructor makes a student object and initialises the map; marks.
Student::Student(const string &name, int regNo) : Person(name) {
this->name = name;
this->regNo = regNo;
// map<string, float> marks; // this line is not needed.
}

c++ get unknown data type input from user

I am new to C++ and I am trying to create a program that gets an unknown data type input from a user and stores it in a variable. I am programming using visual studios and the code keeps producing error messages: E0304, LNK2019 and LNK1120.
#include "stdafx.h"
#include <iostream>
template <typename T>
T dataEntered() {
T data;
std::cout << "Enter Data" << std::endl;
std::cin >> data;
return data;
}
int main()
{
auto data = dataEntered();
std::cout << sizeof(data) << std::endl;
return 0;
}`
Template is not the solution unless you will use it in this way:
auto data1 = dataEntered<char>();
auto data2 = dataEntered<int>();
std::cout << sizeof(data1) << std::endl;
std::cout << sizeof(data2) << std::endl;
You should pass the type parameter so its known at compile time.
If you do not know the data until run-time I recommend that you read the data as string then write a function to parse it as you need.
Templates and autos are deduced at compile time, where autos are basically changed into the type deduced from the initialization value, and a version of the templated version is generated for every type used in the template parameter.
If you need to perform operations on the input, figure out what type the input is before reading it. You can also input it into a string and parse it later.
If you don't need to parse the input, just input into a string.
For example, if the first character of the input is a digit, it's probably a number.
My code:
#include <iostream>
#include <string>
#include <cctype>
int main()
{
if(std::isdigit(static_cast<unsigned char>(std::cin.peek())))
{
//the input is a number
int num;
std::cin >> num;
//do stuff with num
}
else
{
//input is something else
std::string str;
std::cin >> str;
//do stuff with str;
}
}

Scoping issue with a helper method to manipulate a string

I want to use a helper method to perform an operation on a string. I would like the method to have two parameters: input and output. I think there is a scoping problem with how I want to solve the problem because when I try to display the output, it is blank. I've minimized my code so you can see the approach I am taking.
#include <iostream>
#include <string>
using namespace std;
void example_method(string in, string out);
int main(){
string input;
string output;
cin >> input;
example_method(input, output);
cout << "The result is: " << output << endl;
return 0;
}
void example_method(string in, string out){
out = in;
}
This minimization compiles and runs, but no matter what string I enter, the result is always blank.
What would be a better way to approach this problem?
You're passing the output variable to the function, which means a copy of it is being pushed onto the stack, and then the stack variable is being altered and never returned back to the original variable. What you want to do is pass a reference to the variable, Like so:
#include <iostream>
#include <string>
using namespace std;
void example_method(string in, string &out);
int main() {
string input = "";
string output = "";
cin >> input;
example_method(input, output);
cout << "The result is: " << output << endl;
return 0;
}
void example_method(string in, string &out) {
out = in;
}
Solution One: The string output will always be blank, with your code in its present form. This is because output is only being copied to another string instance when it is passed as an argument to example_method. out is the other string instance which takes the value of output. In other words what this means is that string out=output and nothing else. The code line out = in; only copies the value of in to out.
So, actually the value of output is not being acted upon at all.
In order to effect the value of output you have to pass its value by reference or in other words, you have to pass the address of output to example_method and that address is to be taken by a pointer. This way what ever changes are made via the pointer also effect the value of the output string outside the scope of the example_method.
The following code segment illustrates my point:
#include <iostream>
#include <string>
using namespace std;
void example_method(string another_string, string *pointer_to_output);
int main()
{
string input="I am a test string";
string output="";
//cout<<"Enter input string"<<endl;
//cin>>input;
example_method(input,&output);
cout<<"The result is: "<<output<<endl;
return 0;
}
void example_method(string another_string, string *pointer_to_output)
{
*pointer_to_output=another_string;
}
Solution two:
Why not simply change the return type of the example_method from void to std::string? By this approach void example_method(string in, string out); changes to string example_method(string in, string out); in the declaration above the main.
And the returned output string is put out on the screen using cout<<example_method(input, output);
This way you can simply return your output to the screen using cout.
This way your code works, it achieves what you are trying to do and there is no real need of using pointers.
THE MODIFIED CODE:
#include <string>
using namespace std;
string example_method(string in, string out);
int main(){
string input;
string output;
cin >> input;
cout<<example_method(input, output);
// cout << "The result is: " << output << endl;
return 0;
}
string example_method(string in, string out){
out = in;
return out;
}
Hope this helps!
When you pass a argument to a function, the value is copied into the parameter.
In your case, the function will operate on the copied value, and leave the original value untouched (in this case: empty).
This can be changed by declaring the parameter as a 'reference parameter'. This is done by adding a & to the type, like this: string& out. Then no local copy will be made, so the original value will be updated.
Full example:
#include <iostream>
#include <string>
void example_method(std::string in, std::string& out); // note the additional &
int main(){
std::string input;
std::string output;
std::cin >> input;
example_method(input, output);
std::cout << "The result is: " << output << std::endl;
return 0;
}
void example_method(std::string in, std::string& out){ // Note the additional &
out = in;
}
A different approach is to specify a return value for the function.
Your function is returning nothing (void), so you could return the resulting string instead. In that case, the output parameter is no longer required, but you'll have to assign the returned value to the output variable at the calling site.
A complete example:
#include <iostream>
#include <string>
std::string example_method(std::string in); // note that 'output' is gone
int main(){
std::string input;
std::cin >> input;
std::string output = example_method(input);
std::cout << "The result is: " << output << std::endl;
return 0;
}
std::string example_method(std::string in){
return in; // use 'return' to return a value.
}

Unable to take input from a file containing string variables

I decided to make a phonebook in c++ and decided to take input of names, address and number from a file.
So I made a class called contact and declared the public variables name, address and number.
Using a constructor I initialized these to name="noname"(string), number=0(int), address="no address"(string)
Now my body of main goes as:
int main(){
contact *d;
d= new contact[200];
string name,add;
int choice,modchoice;//Variable for switch statement
int phno,phno1;
int i=0;
int initsize=0, i1=0;//i is declared as a static int variable
bool flag=false,flag_no_blank=false;
//TAKE DATA FROM FILES.....
//We create 3 files names, phone numbers, Address and then abstract the data from these files first!
fstream f1;
fstream f2;
fstream f3;
string file_input_name;
string file_input_address;
int file_input_number;
f1.open("./names");
while(f1>>file_input_name){
d[i].name=file_input_name;
i++;
}
initsize=i;
f2.open("./numbers");
while(f2>>file_input_name){
d[i1].phonenumber=file_input_number;
i1++;
}
f3.open("./address");
while(f3>>file_input_address){
d[i1].address=file_input_address;
i1++;
}
now when I later search for a particular entry by name, the name is displayed correctly but the phoneumber is returned as a garbage value and address as "Noaddress"
I dont understand why this is happening...
In case u want to look at entire code, do let me know....
This is how i search for a particular entry which returns the name if matched but returns garbage for phone number....
cout<<"\nEnter the name";//Here it is assumed that no two contacts can have same contact number or address but may have the same name.
cin>>name;
int k=0,val;
cout<<"\n\nSearching.........\n\n";
for(int j=0;j<=i;j++){
if(d[j].name==name){
k++;
cout<<k
<<".\t"
<<d[j].name
<<"\t"<<d[j].phonenumber
<<"\t"<<d[j].address
<<"\n\n";
val=j;
}
}
Thanks in advance
When your reading the file with the phone numbers
f2.open("./numbers");
while(f2>>file_input_name){
d[i1].phonenumber=file_input_number;
i1++;
}
You store the phone number in the string file_input_name but then you use a different var, file_input_number to store the information in the array d;
Hey guys I figured out the problem....
the problem is that i1 should be set to 0 after the second loop
and that the file taking input numbers should be f2.open("numbers") and not names....silly mistake!!
Since you are using C++, and not C, you should take advantage of the things that come with the language. Don't use arrays to store your data, use a std::vector. That way you don't have to remember how many things you have already put into the vector, because you can always ask the vector to tell you the size().
If I had to read in the three files I would go like this:
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <algorithm>
using std::cin;
using std::cout;
using std::fstream;
using std::string;
using std::vector;
class contact {
public:
string name;
string address;
int phone;
};
void print_contact(const contact &c) {
cout << "name " << c.name << " address " << c.address << " phone " << c.phone << "\n";
}
int main(int argc, char **argv)
{
vector<contact> contacts;
string name;
string address;
int phone;
fstream f1("d:\\names.txt");
fstream f2("d:\\phones.txt");
fstream f3("d:\\addresses.txt");
// note that I am using getline() here.
while (getline(f1, name) && f2 >> phone && getline(f3, address)) {
contact c;
c.name = name;
c.address = address;
c.phone = phone;
contacts.push_back(c);
}
for_each(contacts.begin(), contacts.end(), print_contact);
// for the Windows console window
cout << "Press return to continue ...";
string s;
getline(cin, s);
return 0;
}