Unexpected result while passing struct to a function - c++

I want to pass a struct to function something like below (I know i can pass single member to function like input(int age, string s) but i want to pass whole struct like input(student s) )
#include <iostream>
using namespace std;
struct student
{
string name;
int age;
};
void input(student s)
{
cout << "Enter Name: ";
cin >> s.name;
cout << "Enter age: ";
cin >> s.age;
}
int main(int argc, char *argv[]) {
struct student s1;
input(s1);
cout << "Name is: " << s1.name << endl;
cout << "Age is: " << s1.age << endl;
}
Above code does not produce correct output, I want to use above code with pointer so to get expected output.
Test:If i input name to "abc" and age to 10. It does not get printed in main

Your function makes a local copy of the input. It looks like you need to pass by reference:
void input(student& s) { .... }
// ^
By default, function arguments are passed by value, so this issue is not specific to classes. For example,
void increment_not(int i) { ++i; }
int i = 41;
increment_not(i);
std::cout << i << std::endl; // prints 41

Your function passes student s by value, that's why the variable s1 in main doesn't change.
Change it to pass reference:
void input(student& s)
// ^

You need to pass the struct by reference, rite now you are passing it by copy so whatever changes are made they are on copy of the passed object.
void input(student& s){....}

Related

Get values from user from different cin using a template

I have to create the required function template prompt which displays a supplied string and then returns a value of the templated type. Finally, add a line which calls this function with the string "What is the answer? " (note the trailing space) and stores this answer in the misc field of the supplied struct.
I have no idea what I am doing, please help!
#include <string>
#include <iostream>
using std::string;
struct Answers {
string name;
float answer;
int misc;
};
// write template function "prompt" here
**template <class T>
void prompt(string prompt_question, T& answer)
{
prompt_question = "What is the answer? ";
cin >> answer;
misc = answer;
}**
// what should I have written?
int main(int argc, char* argv[])
{
**using namespace std;
Answers info {
prompt<string>("What is your name? "),
prompt<float>(argv[2]),
};**
cout << '\n' << "Who: " << info.name;
cout << '\n' << "Knowledge: " << info .answer;
cout << '\n' << "Wisdom: " << info.misc;
return 0;
}
I feel stupid for not knowing how to solve it, been trying to figure it out since 7 est. Please help
Things between ** are the only things I can edit unfortunately
IMHO, that attempt of OP was not so lucky.
I try to sort it out:
template <class prompt> prompt(string prompt_question)
That's broken. The identifier of function is missing. Or, may be, the return type of function is missing but then the name of the function is the same as the identifier of template parameter.
prompt_question = answer;?
What shall this be good for?
answer is not declared in this scope nor initialized.
Why prompt_question shall be overridden with it?
answer = "What is the answer??
What shall this be good for?
misc = answer;?
What shall this be good for?
misc is not declared in this scope.
My working sample to show how this could look like:
#include <cassert>
#include <iostream>
struct Answers {
std::string name;
float answer;
int misc;
};
template <class T>
void prompt(const std::string &question, T &answer)
{
std::cout << question;
std::cin >> answer;
}
int main()
{
Answers info { };
prompt("Who: ", info.name);
if (!std::cin) { std::cerr << "Input failed!\n"; return -1; }
prompt("Age: ", info.answer);
if (!std::cin) { std::cerr << "Input failed!\n"; return -1; }
std::cout << "\nYou claim to be " << info.name << " with an age of " << info.answer << ".\n";
}
Output:
Who: Just
Age: 34
You claim to be Just with an age of 34.
Live Demo on coliru
After Edit it seems, the OP requires that template parameter is the return type:
#include <cassert>
#include <iostream>
struct Answers {
std::string name;
float answer;
int misc;
};
template <class T>
T prompt(const std::string &question)
{
std::cout << question;
T answer;
std::cin >> answer;
return answer;
}
int main(int argc, char **argv)
{
Answers info {
prompt<std::string>("Who: "),
prompt<float>("Age: ")
};
if (!std::cin) { std::cerr << "Input failed!\n"; return -1; }
std::cout << "\nYou claim to be " << info.name << " with an age of " << info.answer << ".\n";
}
Output:
Who: Just
Age: 34
You claim to be Just with an age of 34.
Live Demo on coliru
Comparing first and second approach, you will notice a design weakness of the second:
While in the first approach, the type can be deduced by the compiler, this is not possible in the second. The only difference of template instances of second template function prompt() will be the return type – not deducible for compiler. Hence, the second template function must be used with an explicit template parameter always (which introduces another opportunity to make something wrong).
A general recommendation:
When you attempt to write a template function and you are uncertain about templates then start with a plain function, and a typedef for the type which shall become a template parameter:
typedef std::string T;
void prompt(const std::string &question, T &answer)
{
std::cout << question;
std::cin >> answer;
}
Compiling, testing, admiring, done.
Now, it can be turned into a template function:
//typedef std::string T; // obsolete
template <class T>
void prompt(const std::string &question, T &answer)
{
std::cout << question;
std::cin >> answer;
}
Compiling, testing, admiring, done.
If I understand correctly you can use operator>> overloading:
struct AnimalNameAnswer
{
std::string animalName;
};
std::istream& operator>>(std::istream& in, AnimalNameAnswer& ana)
{
return in >> ana.animalName;
}
struct CarSpeedAnswer
{
int MPH;
};
std::istream& operator>>(std::istream& in, CarSpeedAnswer& csa)
{
return in >> cas.MPH;
}
template<class ANSWER>
void prompt(ANSWER& a)
{
std::cout << "Enter answer:" << std::endl;
std::cin >> a;
}

Passing array of strings to a function

I am trying to create a program that uses class, arrays, and functions to show information about two students(Name, id#, classes registered). The part I am struggling with is passing arrays to a function. How do I do that?
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
class Student // Student class declaration.
{
private:
string name;
int id;
string classes;
int arraySize;
public:
void setName(string n)
{
name = n;
}
void setId(int i)
{
id = i;
}
void setClasses(string c, int num)
{
classes = c;
arraySize = num;
}
string getName()
{
return name;
}
int getId()
{
return id;
}
void getClasses()
{
for (int counter=0; counter <arraySize; counter++) {
cout << classes[counter] << endl;
}
}
};
int main()
{
//Student 1
string s1Name = "John Doe";
int s1Id = 51090210;
int const NUMCLASSES1 = 3;
string s1Classes[NUMCLASSES1] = {"C++","Intro to Theatre","Stagecraft"};
//Student 2
string s2Name = "Rick Harambe Sanchez";
int s2Id = 666123420;
int const NUMCLASSES2 = 2;
string s2Classes[NUMCLASSES2] = {"Intro to Rocket Science","Intermediate Acting"};
//
Student info;
info.setName(s1Name);
info.setId(s1Id);
//info.setClasses(s1Classes, NUMCLASSES1);
cout << "Here is Student #1's information:\n";
cout << "Name: " << info.getName() << endl;
cout << "ID: " << info.getId() << endl;
//cout << "Classes: " << info.getClasses() << endl;
info.setName(s2Name);
info.setId(s2Id);
// info.setClasses(s2Classes, NUMCLASSES1);
cout << "\n\nHere is student #2's information:\n";
cout << "Name: " << info.getName() << endl;
cout << "ID: " << info.getId() << endl;
//cout << "Classes: " << info.getClasses() << endl;
return 0;
}
The usual way to pass around variable-length lists in C++ is to use an std::vector. A vector is a single object that you can easily pass to a function, copying (or referencing) its contents. If you are familiar with Java, it's basically an ArrayList. Here is an example:
#include <vector>
#include <string>
using namespace std;
class foo {
private:
vector<string> myStrings;
public:
void setMyStrings(vector<string> vec) {
myStrings = vec;
}
}
//...
foo myObj;
vector<string> list = {"foo","bar","baz"};
myObj.setMyStrings(list);
If don't want to use the standard library though, you can pass an array C-style. This involves passing a pointer to the first element of the array, and the length of the array. Example:
void processStrings(string* arr, int len) {
for (int i = 0; i < len; i++) {
string str = arr[i];
//...
}
}
string array[] = {"foo","bar","baz"};
processStrings(array, 3); // you could also replace 3 with sizeof(array)
Passing raw arrays like this, especially if you wanted to then copy the array into an object, can be painful. Raw arrays in C & C++ are just pointers to the first element of the list. Unlike in languages like Java and JavaScript, they don't keep track of their length, and you can't just assign one array to another. An std::vector encapsulates the concept of a "list of things" and is generally more intuitive to use for that purpose.
Life lesson: use std::vector.
EDIT: See #nathanesau's answer for an example of using constructors to initialize objects more cleanly. (But don't copy-paste, write it up yourself! You'll learn a lot faster that way.)
You can pass array of any_data_type to function like this
void foo(data_type arr[]);
foo(arr); // If you just want to use the value of array
foo(&arr); // If you want to alter the value of array.
Use std::vector. Also, don't add functions you don't need. Here's an example of using std::vector
#include <string>
#include <iostream>
#include <vector>
using std::string;
using std::vector;
class Student // Student class declaration.
{
private:
vector<string> classes;
string name;
int id;
public:
Student (const vector<string> &classesUse, string nameUse, int idUse) :
classes (classesUse),
name (nameUse),
id (idUse)
{
}
void print ()
{
std::cout << "Name: " << name << std::endl;
std::cout << "Id: " << id << std::endl;
std::cout << "Classes: ";
for (int i = 0; i < classes.size (); i++)
{
if (i < classes.size () - 1)
{
std::cout << classes[i] << ", ";
}
else
{
std::cout << classes[i] << std::endl;
}
}
std::cout << std::endl;
}
};
int main()
{
Student John ({"C++","Intro to Theatre","Stagecraft"},
"John",
51090210);
John.print ();
Student Rick ({"Intro to Rocket Science","Intermediate Acting"},
"Rick",
666123420);
Rick.print ();
return 0;
}
Name: John
Id: 51090210
Classes: C++, Intro to Theatre, Stagecraft
Name: Rick
Id: 666123420
Classes: Intro to Rocket Science, Intermediate Acting
In the private variables of class Student, you are storing a string:
String classes;
where as you should be storing an array of strings like:
String classes[MAX_NUM_CLASSES];
then in the set classes function, pass in an array of strings as the first argument, so it should be :
void setClasses(string[] c, int num)
{
classes = c; //not sure if simply setting them equal will work, rather copy entire array using a for loop
arraySize = num;
}
This should point you in the right direction
Also, use std::vector instead of string[], it will be easier.

In C++, how do I ask the user to input a string, have that string stored, and be able to recall it in any/many functions?

I want to be able to have the user enter their name, store it and be able to recall it in different functions. This is my first code, first program. I am sure there is an easier way to do this, so if you could offer both an answer to the question and a easier way of accomplishing this task it would be much appreciated. Thank you in advance.
This is what I have so far:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
using namespace std;
void game();
void other();
class NameClass{
public:
string name;
};
int main()
{
int a;
int age;
string name;
cout << "Hello user, what is you name? \n\n";
/*Not sure if class and operator needs to be here. I was hoping that when the user
input the stream(their name) it would be stored in the class as well as being able
to use it in the main function.*/
NameClass person;
//This line is here to get name from user.
getline(cin, name);
cout << "Well " << name << ", are you having a good day? \n\n";
cout << "1=Yes 2=No \n\n";
cin >> a;
cout <<"\n";
if(a==1){
cout << "Well that is good to hear.\n\n";
}else{
cout << "I am sorry to hear that. I hope things get better for you. \n\n";
}
cout << "Do you want to play a game? \n\n";
cin >> a;
if(a==1){
game();
}else{
other();
return 0;
}
return 0;
}
void game(){
/*It is in this function that I want the be able to recall the name that the user input in the main function.*/
cout << "Cool "<< name <<",let get started." << endl;
}
void other(){
cout << "Well then "<< name <<", lets do something else.";
}
I assume you aren't aware of the OOP concepts.
Do this (make methods part of the class):
class NameClass{
public:
string name;
void game();
void other();
};
int main()
{
...
NameClass person;
getline(cin, person.name);
...
}
Or this (pass name as parameter):
int main()
{
string name;
...
game(name);
}
void game(string name)
{
cout << "Cool "<< name <<",let get started." << endl;
}
If you want to pass information from one function to another then you use a function parameter (or more than one).
void game(string name);
int main()
{
string name;
...
game(name);
}
void game(string name)
{
cout << "Cool "<< name <<",let get started." << endl;
}
It's a fundamental concept that pretty much all programming languages have.
Just pass the name as a parameter in both your functions, it should be something like this:
//functions
void game(string name);
void other(string name);
In your main function when you call either function just pass the name to it.
if(a == 1)
{
game(name);
}
else
{
other(name);
}

C++ No instance of overloaded function getline matches argument list

I'm writing a program which needs to read input from cin into a string. When I tried using the regular getline(cin, str) it endlessly prompted for input and never moved on the the next line of code. So I looked in my textbook and it said I could pass a cstring and size of the string to getline in the form of cin.getline(str, SIZE). However, when I do that I get the error "no instance of overloaded function getline matches the argument list.
I searched around, but all I found was people saying to use the getline(cin,str) form which led to the infinite input prompt, or the suggestion that there might be two different getline functions with different parameters in the classes I'm including, and that I need to tell the IDE to use the correct one(I'm not sure how to do that).
This is what I include at the beginning of my file:
#include <string>
#include <array>
#include <iostream>
#include "stdlib.h"
#include "Bank.h" //my own class
using namespace std;
And this is the relevant section of code:
const int SIZE = 30; //holds size of cName array
char* cName[SIZE]; //holds account name as a cstring (I originally used a string object in the getline(cin, strObj) format, so that wasn't the issue)
double balance; //holds account balance
cout << endl << "Enter an account number: ";
cin >> num; //(This prompt works correctly)
cout << endl << "Enter a name for the account: ";
cin.ignore(std::numeric_limits<std::streamsize>::max()); //clears cin's buffer so getline() does not get skipped (This also works correctly)
cin.getline(cName, SIZE); //name can be no more than 30 characters long (The error shows at the period between cin and getline)
I'm using Visual Studio C++ 2012, if that's relevant
This is the offending line:
char* cName[SIZE];
What you really need here is:
char cName[SIZE];
Then, you should be able to use:
cin.getline(cName, SIZE);
This error message from visual studio is quite misleading. Actually for me I was trying to call a non const member function from const member function.
class someClass {
public:
void LogError ( char *ptr ) {
ptr = "Some garbage";
}
void someFunction ( char *ptr ) const {
LogError ( ptr );
}
};
int main ()
{
someClass obj;
return 0;
}
Related to the answer of R Sahu the compiler gives a clue that the argument does not match.
Try the following code which does not give the error and makes the error message more clear.
#include <iostream>
class someClass {
public:
void LogError(char* ptr) {
std::cout << "LogError: before arg=" << ptr << "\n";
ptr[0] = 0;
std::cout << "LogError: after arg=" << ptr << "\n";
}
void LogError(char* ptr) const {
std::cout << "const LogError: arg=" << ptr << "\n";
}
void ROFunction(char* ptr) const {
LogError(ptr);
}
void RWFunction(char* ptr) {
LogError(ptr);
}
};
int main()
{
char s[] = "test";
someClass obj;
obj.ROFunction(s);
obj.RWFunction(s);
obj.ROFunction(s);
return 0;
}
With the output:
const LogError: arg=test
LogError: before arg=test
LogError: after arg=
const LogError: arg=

error pushing structure objects with string members into a std::vector

I'm a C#, C programmer learning C++ and I'm having a bit of a trouble.
I'm trying to push an object of the struct type 'Person' into a vector but the string values which are members of the Person type are not copied. Also the code exits with an error message - posted at the bottom:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef struct Person {
string Name;
string Lastname;
int Age;
} Person;
void CreatePerson(Person* in_person, string in_name, string in_last,
int in_age)
{
Person t_person;
t_person.Name = in_name;
t_person.Lastname = in_last;
t_person.Age = in_age;
memcpy(in_person, &t_person, sizeof(t_person));
}
int main(int argc, char *argv[])
{
vector<Person> people;
Person t_ppl;
CreatePerson(&t_ppl, "Zareh", "Petros", 13);
people.push_back(t_ppl);
CreatePerson(&t_ppl, "Tina", "Yarroos", 26);
people.push_back(t_ppl);
int ii;
for(ii=0; ii < people.size() ; ii++) {
cout << "Element - " << ii << endl;
cout << "name:" << people[ii].Name << endl;
cout << "lastname:" << people[ii].Lastname << endl;
cout << "age:" << people[ii].Age << endl;
}
return 0;
}
And here is the error message:
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x09d48048 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75ee2)[0xb74e8ee2]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xb76e551f]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b)[0xb76cc99b]
/usr/lib/i386-linux-gnu/libstdc++.so.6(+0x909dc)[0xb76cc9dc]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x2e)[0xb76cca4e]
std::string is a class and should not be copied by a memcpy operation. It may hold instance-specific data, which will get scrambled if held by two different instances (and probably that's the cause of your problems).
Imagine, that std::string is something like:
class string
{
private:
char * data;
int dataLength;
};
If you memcpy one string to another, both data and dataLength are copied to another place (lately being treated as a normal string instance). However, when the destructor is called on these strings (when they run out of scope), they will attempt to free the data held in data field. First string (which is an actual owner of this pointer) will free memory pointed to by this pointer. But then another destructor of your copied string will run and will try to free this memory again, what is not permitted.
Note, that it's exactly, what your system is reporting: double freeing of memory.
Your code is very C-style. In C++ one would create a class with constructor rather than a function, which fills in a struct. I would write your code in the following way:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
using namespace std;
struct Person
{
public:
string Name;
string Lastname;
int Age;
Person(string newName, string newLastname, int newAge)
: Name(newName), Lastname(newLastname), Age(newAge)
{
}
};
int main(int argc, char *argv[])
{
vector<Person> people;
Person person1("Zareh", "Petros", 13);
people.push_back(person1);
Person person2("Tina", "Yarros", 26);
people.push_back(person2);
for(unsigned int i=0; i < people.size() ; i++)
{
cout << "Element - " << i << endl;
cout << "name:" << people[i].Name << endl;
cout << "lastname:" << people[i].Lastname << endl;
cout << "age:" << people[i].Age << endl;
}
getchar();
return 0;
}
The role of your creation method takes the class constructor. It fills in fields of the class properly. Also, C++ provides default copy-constructor and assignment operator, which will handle assigning one person to another properly.
Some side notes about your code style.
In C++ avoid using memcpy. If you have a need of using it, you probably should consider creating proper copy constructor, std::copy or simply making an assignment (what would work perfectly in your situation). memcpy should be used only when copying raw chunks of memory.
typedef is no longer required for structs in C++. Instead of writing:
typedef struct Name { ... } Name;
You can simply write:
struct Name { ... };
You've already been told you shouldn't be using memcpy in this situation, so I won't bother repeating that.
The problem with your CreatePerson goes well beyond using memcpy, and just changing to std::copy isn't really going to make it right.
Instead of a free function to create a person, you should almost certainly write that functionality as a constructor instead:
struct Person {
string Name;
string Lastname;
int Age;
Person(string Name, string Last, int Age)
: Name(Name), LastName(Last), Age(Age)
{}
};
With this, we can create Person objects much more cleanly:
std:::vector<Person> people;
people.push_back(Person("Zarah", "Petros", 13));
people.push_back(Person("Tina", "Yarroos", 26));
I'd also write an inserter that's responsible for displaying a Person in the proper format:
std::ostream &operator<<(std::ostream &os, Person const &p) {
return os << "Name: " << p.Name < "\n"
<< "Last: " << p.LastName << "\n"
<< "Age: " << p.Age << "\n";
}
With this, your mainstream of your code can insert complete Person objects into a stream without paying attention to the internal details of what a Person contains or how it should display:
for (int i=0; i<people.size(); i++)
std::cout << people[i] << "\n";
If you want to be a little more ambitious, you can use a standard algorithm instead:
std:copy(people.begin(), people.end(),
std::ostream_iterator<Person>(std::cout, "\n"));
Or, if you're using a relatively new compiler, you can use a range-based for loop:
for (auto &p : people)
std::cout << p << "\n";
Putting all that together, your complete program ends up something like this:
#include <string>
#include <iostream>
#include <vector>
using std::string;
struct Person {
string Name;
string LastName;
int Age;
Person(string Name, string Last, int Age)
: Name(Name), LastName(Last), Age(Age)
{}
};
std::ostream &operator<<(std::ostream &os, Person const &p) {
return os << "Name: " << p.Name << "\n"
<< "Last: " << p.LastName << "\n"
<< "Age: " << p.Age << "\n";
}
int main(){
std::vector<Person> people;
people.push_back(Person("Zarah", "Petros", 13));
people.push_back(Person("Tina", "Yarroos", 26));
for (auto &p : people)
std::cout << p << "\n";
return 0;
}
Do not use memcpy() on non-POD types. It does not call copy-constructors.
Use std::copy() instead.
In this case, it is easier to do an assignment. Replace:
memcpy(in_person, &t_person, sizeof(t_person));
with
*in_person = t_person;