c++ char array output in class functions - c++

I am a real c++ beginner and I have a problem with my char array output in a c++ excerise. I was asked to transform a certain UML class in to c++ and generate an working output with the parameters given in main. Here ist the code:
#include <iostream>
#include <stdlib.h>
/*My class defintion book*/
class Book
{ protected:
long int number;
char author[25];
int year;
bool lent;
void setLent(bool x);
bool getLent();
public:
Book(long int n, char a[25], int j, bool x);
long int getNr();
int getYear();
void print();
};
/*Method definition Book*/
Book::Book(long int n, char a[25], int j, bool x)
{number=n;
author=a;
year=j;
lent=x;}
long int Book::getNr()
{return number; }
int Book::getYear()
{return year;}
void Book::setLent(bool x)
{lent=x;}
bool Book::getLent()
{return lent;}
void Book::print()
{
std::cout << "Book Nr: " << number << std::endl;
std::cout << "Author: " << author << std::endl;
std::cout << "Year: " << year << std::endl;
if (lent==0)
std::cout << "Lent [yes/no]: no" << std::endl;
else
std::cout << "Lent [yes/no]: yes" << std::endl;
}
/*MAIN*/
int main()
{
Book b1(123456, "test", 2014, false);
b1.print();
system("pause");
return 0;
This is my output:
Book Nr: 123456
Author: b<Vv-[[vóYA
Year: 2014
Lent [yes/no]: no
Press any key to continue...
As you can see all outputs work except for the "Author". There I am getting crap. Note that I have to use char as type. since it is given in the UML class I had to transform into c++.
I really searched everywhere. But didn't find the correct solution. I have the feeling it will be a very simple one...
Thanks in advance for your help!

The reason this doesn't work is that you're assigning your pointer author to another pointer a, which then goes out of scope... so you're left with author pointing to some garbage. If you want to stick with character arrays, you'll have to copy all the data that a points to:
strcpy(author, a);
But since it's C++, you should just use strings, which are easier to deal with:
class Book {
...
std::string author;
....
};
Book::Book(long int n, const std::string& a, int j, bool x)
: author(a), ...
{ }

You are printing out uninitialized data.
Make author a string
#include <string>
class Book
{ protected:
long int number;
std::string author;
int year;
bool lent;
and make the argument to the constructor a string as well
Book::Book(long int n, const std::string& a, int j, bool x)
Arrays of characters are not as flexible as std::strings. they are just chunks of data. If you want to use strings then use std::string instead.
Also, use an initializer list in C++ constructors, not java style
Book::Book(long int n, const std::string &a, int j, bool x)
: number(n),
author(a),
year(j),
lent(x)
{ }

There are two bugs in your code:
Book::Book(long int n, const char a[25], int j, bool x)
{
number=n;
strncpy(author, a, 25); // author = a; doesn't work! shouldn't compile either...
year=j;
lent=x;
}
First: The variable author is a pointer to a zero terminated string. You can use strcpy() to copy this string. Therefore you need to #include <memory.h. But you need to be sure that the string -is- really zero-terminated and fits into your target variable! Else you'll overwrite other memory regions next to the target variable, which is also called a buffer overflow! Better use strncpy(target, source, maxlength); which avoids this problem.
Second: Your parameter a should be "const" as you want to be able to call it with a string constant like in Book b1(123456, "test", 2014, false); where "test" is a constant!
As others already suggested you should use std::string instead of a[25]. C-Strings are "C" and not "C++" and you should try to avoid them. C-Strings can introduce a lot of bugs into your code and enable buffer overflows (=security problems), too. Also they are more complicated to handle. You need to #include <string> to use them.

Related

How to construct a string by padding OR truncating another input string?

There are millions of answers about strings, but I cannot find one that fits my problem. I tried finding it in cppreference.com, but I cannot find a solution for this specific problem.
I know how to solve the problem in general - it works. But I want to learn more C++ and find a "proper" (efficient) solution. Maybe one of the C++ wizards can help me out :)
I have a struct that contains a double and a string (the name of a variable, for example). When I want to initialize the struct through the constructor, I want to make sure that the string in the struct has a fixed length. If it is too long, I want to truncate it, if it is too short, I want to pad it. I can use resize() for that.
I am searching for a way to efficiently do that in one step in the initializer list of the constructor, if possible. Is there some clever operation I am missing?
All I can come up with is to write it in the function body of the constructor:
#include <iostream>
#include <string>
using namespace std;
const uint STRING_LENGTH = 25;
struct parameter {
double value;
string name;
parameter(double _value, const string& _name)
: value(_value) // easy
{
string name_temp(_name);
name_temp.resize(STRING_LENGTH, ' ');
name = name_temp;
};
};
int main(int argc, char* argv[]) {
parameter A(0.5, "my_variable");
parameter B(-0.1, "my other variable with the name that is too long and has to be truncated!");
cout << "parameter name: " << A.name << "\tis:\t" << A.value << '\n';
cout << "parameter name: " << B.name << "\tis:\t" << B.value << '\n';
}
I like the resize trick, but removing the const& (so that the caller can std::move() the string in if they want to) might be a slightly better interface. Here there will be at most one copy in the actual function body (assuming the caller moved the string in), whereas in the original version there were three in the worst case.
parameter(double _value, string _name)
: value(_value), name(std::move(_name))
{
name.resize(STRING_LENGTH, ' ');
};
If you really want to handle this in the constructor's initialization list, write a helper function that it can call, eg:
std::string&& fix_length(std::string &&s) {
s.resize(STRING_LENGTH, ' ');
return std::move(s);
}
struct parameter {
double value;
std::string name;
parameter(double _value, std::string _name)
: value(_value), name(fix_length(std::move(_name)))
{
}
};
Online Demo
However, if you really need a fixed-length string, consider using std::array instead, eg:
class fixedString
{
std::array<char, STRING_LENGTH+1> cdata;
public:
fixedString(const std::string &s)
: fixedString(s.c_str(), s.size())
{
}
fixedString(const char *s)
: fixedString(s, std::strlen(s))
{
}
fixedString(const char *s, const size_t len)
{
std::copy_n(s, std::min<size_t>(len, STRING_LENGTH), cdata.begin());
if (len < STRING_LENGTH)
std::fill_n(cdata.begin()+len, STRING_LENGTH-len, ' ');
cdata[STRING_LENGTH] = '\0';
}
friend std::ostream& operator<<(std::ostream& os, const fixedString &fs)
{
os << fs.cdata.data();
return os;
}
};
struct parameter {
double value;
fixedString name;
parameter(double _value, const fixedString &_name)
: value(_value), name(_name)
{
}
};
Online Demo

Trying to use a member function on a class array

Using the code below, I want to use my sort function to take an Student array and sort them based on their gpa component. I have to use the parameters of a Student array and the size of the array. If you look towards the bottom of my int main function I try to call on the member sort to sort the array a but with no avail. The error I get is:
member reference base type Student [200] is not a structure or union.
How do I wrote my code to take array a and say to use the member Sort on it given the parameters I have to use. Thanks in advance. If this too much please let me know I'll try to specify even more.
class Student
{
private:
string ID, fname, lname, level;
double gpa;
public:
Student();
Student(string id, string first, string last, double Gpa, string grade);
string getID() const;
string getfirst() const;
string getlast() const;
string getlevel() const;
double getGPA() const;
void setID(string id);
void setfirst(string f1);
void setlast(string l1);
void setlevel(string lev);
void setGPA(double GPA1);
friend void Sort(Student studentlist[], int size);
friend ostream& operator <<(ostream& ost, Student S1);
};
int main()
{
ifstream ins;
ofstream outs;
ins.open("input.dat");
outs.open("output.dat");
if(ins.fail())
{
cout << "File not found.";
exit(1);
}
if(outs.fail())
{
cout << "Output file not opened.";
exit(1);
}
Student a[200];
int x = 0;
while(!ins.eof())
{
string id, fnam, lnam, levl;
double point;
ins >> id >> fnam >> lnam >> point >> levl;
a[x].setID(id);
a[x].setfirst(fnam);
a[x].setlast(lnam);
a[x].setGPA(point);
a[x].setlevel(levl);
if(a[x].getID() == "")
{
break;
}
x += 1;
}
if(x == 0)
{
cout << "File is empty" << endl;
return 0;
}
x = x +1;
a.Sort(a, x);
int t=0;
while(t<x)
{
outs << a[t];
t += 1;
}
outs.close();
ins.close();
return 0;
}
Get rid of the a.. Since Sort is a free function, you need just
Sort(a, x);
In C++, arrays are not class objects, so there is no Sort method like there is in C#, however you can use std::sort:
using namespace std;
Student array[200];
// (populate `array` here)
sort(
begin(array),
end(array),
[](const Student& x, const Student& y) -> bool {
return x.gpa > y.gpa;
}
);
I recommend using std::Array<T> instead of "raw" arrays for greater runtime safety and to avoid needing to keep track of the array length separately:
I note that you're storing Student objects as values, not pointers, so "moving" a Student to another index in the array will be expensive because it will copy the entire object. Consider allocating Students separately and only sorting an array of Student* pointers instead.
Use of
a.Sort(a, x);
is incorrect on couple of accounts.
a is an array type, specifically of type Student [200]. Arrays don't have member functions. Hence, use of a. is not allowed.
Sort is a non-member function. Hence it cannot be called with the .Sort() syntax.
Just use:
Sort(a, x);

Dynamicly setting which parameter to extract

Having structure
struct Person{
Person(int a , int i):age(a),id(i){};
int age;
int id;
}
Is it possible to pass which argument to exctract as argument in function? Something like
int extract( Person * p , param ){
return p -> param;
}
which would return id , if used it like
extract( p , "id" )
and age if i used it like
exctract(p , "age")
Is something like this possible in c++?
You can use pointers to class members.
struct Person{
Person(int a , int i):age(a),id(i){};
int age;
int id;
};
int extract(Person* p, int Person::* param)
{
return p->*param;
}
and you'd use it like this:
extract(p, &Person::id);
demo
You can do it with preprocessor abuse, and more notably, without using string comparisons, which seems like what you want to do.
#include <iostream>
#define extract(p, i) (p->i)
struct Person{
int age;
int id;
};
int main() {
Person p;
p.age = 100;
p.id = 30;
std::cout << extract((&p), id) << '\n' << extract((&p), age) << '\n';
}
Not that I suggest doing this, though.
You can use Map<char*, int>which can do exactly that (but arguably is a bit of owerkill).
Or just plain char[][] and check equality with the parameter in a for loop.

Char passing in C++ with pointers

Im having unclear image of pointers and char passing with functions. please anyone can tell me where im doing wrong and brief idea about pointers?ex : where should i use them, etc...
#include <iostream>
using namespace std;
class book{
private:
char *CName;
int CFee;
int NoPeople;
int Income;
public:
void setData(char &x,int y,int z){
CName = &x;
CFee = y;
NoPeople = z;
}
void calIncome(){
Income = CFee * NoPeople;
}
void viewIncome(){
cout<<Income;
cout<<CName;
}
};
int main(){
book b1;
b1.setData('DISE',20000,30);
b1.calIncome();
b1.viewIncome();
}
im getting error in this code
//b1.setData('DISE',20000,30); "non-const lvalue reference to type 'char' cannot bind to a temparory of type 'int'"
In your code there is no need for pointers. You should use std::string:
#include <string>
...
string CName
...
void setData(const string& x,int y,int z){
CName = x;
and in setData call you should use double quotes (which are for strings) instead of single quotes (which are for individual characters).
You should change setData() declaration to void setData(const char *x,int y,int z)
As you're currently doing you are expecting a reference to a single char as parameter, which cannot be used to assign a char* pointer that is meant to point at a character array.
Also you aren't specifying a character array literal in the call:
b1.setData('DISE',20000,30);
Needs to be changed to
b1.setData("DISE",20000,30);
// ^ ^
b1.setData('DISE',20000,30);
char is only one char, like 'D', if you have multiple char, that is string, and you need to pass it as "DISE"
With your method signature, void setData(char &x,int y,int z) you can only pass char. That is one character.
setData metod is completely useless here. Its work should be done by a constructor, and the name variable (which should NOT be named CName should be an std::string. viewIncome should automatically call calIncome and a dirty flag should probably be introduced. Otherwise calIncome should be a free/static function and the income member should be removed. The function parameters should also be reasonably captioned.
And I'll even answer the question:
class Book {
std::string name;
int fee;
int noPeople;
int income;
public:
Book(std::string name, int fee, int noPeople) :
name(std::move(name)),
fee(fee),
noPeople(noPeople)
{
}
void calIncome() {
income = fee * noPeople;
}
void viewIncome() {
calIncome();
std:: cout << income << name;
}
};
int main() {
Book b1 ("DISE", 20000, 30);
b1.viewIncome();
}
See it live on Coliru.

No suitable constructor to convert from int to emp

My program simply is to increase the salary int the emp class throw the the function increase
but I'm having this error int the call function line from the line :
No suitable constructor to convert from int to emp
here 's my code :
#include <iostream>
#include <string>
using namespace std;
class emp
{
public:
int salary;
};
void increase(emp x,emp y)
{
x.salary+=100;
y.salary+=250;
}
int main()
{
int value=0;
emp fst, scnd;
cin >> fst.salary >> scnd.salary;
increase(fst.salary,scnd.salary);
cout << fst.salary << endl << scnd.salary << endl;
cin >> value;
return 0;
}
increase expects two emps as parameters, yet you pass in two ints.
Change
increase(fst.salary,scnd.salary);
to
increase(fst,scnd);
Your next question is going to be why the values don't change, so to save you the trouble - it's because you're passing by value, effectively changing copies of your original objects. You'll need to pass by reference:
void increase(emp& x,emp& y)
increase(fst.salary,scnd.salary); should be increase(fst,scnd);, void increase(emp x,emp y) ... should be void increase(emp& x,emp& y) ...
You need to pass emp not int. Further, you are passing parameters by value. Use this instead:
void increase(emp &x,emp &y)
And pass the struct variables; i.e. fst and scnd instead of fst.salary and scnd.salary. Refer this question for better understanding.