a special Person class in C++ - c++

I am a newbie in C++ as I am learning it at college, and I have a problem. I have a project to do that should be fairly easy to do, but I seem to encounter some difficulties. I must implement a Person class, with exactly 3 arguments: name, firstnames (this is my BIG problem because there can be several names put in an array of char*, so it will be a char**) and age. My teacher gave me a testPerson.cc file in which it uses my Person class to create several types of Persons. My problem is when I create the constructor(s), because I must manage several cases: for example if a person has only one first name, for example:
const Personne lea ("Tralala", "Lea", 45);
or a person has several firstnames:
const char* prenoms1[] = {"Marcel", "Firmin", "Gaston", 0};
const Personne marcel ("Meharcele", prenoms1, 78);
I know for sure that I must have exactly 3 attributes: name(char*), firstname(char**), age(int).
Here is a snippet from the file that the teacher gave me(which I must respect when creating my Person class):
int main () {
cout << "We create the next persons:\n";
// version of constructor with several names:
const char* prenoms1[] = {"Marcel", "Firmin", "Gaston", 0};
const Personne marcel ("Meharcele", prenoms1, 78);
// version of constructor with only one name:
const Personne lea ("Tralala", "Lea", 45);
As you can see, I need several constructors for cases with only 1 fname, or several fnames
And here is my class:
#include "personne.h"
Personne::Personne(const char* name, const char** fnames, int a) {
nom = name;
prenom = fnames;
age = a;
}
Personne::Personne(const char* name, const char* fname, int a) {
nom = name;
prenom = fname; //here I have a problem, since the attribute prenom is of type char**
age = a;
}
void Personne::setAge(int& a) {
age = a;
}
void Personne::setNom(const char* name) {
nom = name;
}
void Personne::setPrenoms(const char** fnames) {
}
int Personne::getAge() const {
return age;
}
char* Personne::getNom() const {
return nom;
}
char** Personne::getPrenoms() const {
return prenom;
}
I spent hours and hours thinking about a solution, I googled a lot(so I did my homework), it's just that I do not know how to implement the right solution.

Some considerations:
a single first name can be seen as an array of one element so you can still use an array to internally store the list of first names, and let it be of size 1 when there is only one.
in your constructor you are assigning pointers which are allocated on stack in the caller of your constructor, while this doesn't create problems in your specific snipped, it isn't the right way to do it: you should create your own copy of each value of the person so that they don't get lost once the parameters are lost and manage their destruction in the Personne destructor ~Personne()
I don't know if you are allowed to use STL library, in that case consider using string to store names and vector<string> list of names, they will do most of the dirty work for you

Related

C++ invalid conversion from 'const char*' to char*

I am new to C++. I have an exercise about constructor with const char* parameter.
class Book
{
private:
char* title;
}
public:
Book (const char* title)
{
this->title = title;
}
When I code the constructor like that, I receive error cannot convert const char to char*.
I tried using
strcpy(this->title, title);
then, it run, but I don't get the expected result.
Can anyone help me.
Thank you very much.
You are doing an exercise, did the course you are following not explain what you are supposed to do?
I would guess that this is an exercise in dynamic memory allocation and what you are expected to do is use strcpy after you have allocated some memory so that you have somewhere to copy the string to. Like this
this->title = new char[strlen(title) + 1];
strcpy(this->title, title);
You need to allocate one extra char because C style strings are terminated with an additional nul byte.
But any C++ programmer who was doing this for real (instead of it being a learning exercise) would use a std::string as paxdiablo says.
C++ doesn't let you easily change a const char * into a char * since the latter has no restrictions on how you can change the data behind that pointer.
If you're doing C++, you should be avoiding char * (legacy C) strings as much as possible. By all means take them as parameters if you must, but you should be turning them into C++ strings at the earliest opportunity:
class Book {
private:
std::string m_title;
public:
Book (const char *title) {
m_title = title;
}
};
The one thing you don't want to become is a C+ developer, that strange breed that never quite made the leap from C across to the C++ way of thinking :-)
And, actually, if a given book is never expected to change its title, you're better off making it constant and initialising it, rather than assigning to it, something like:
#include <iostream>
#include <string>
class Book {
public:
Book (const char* title): m_title(title) {};
void Dump() { std::cout << m_title << "\n"; }
private:
const std::string m_title;
};
int main() {
Book xyzzy("plugh");
xyzzy.Dump();
}

having an array as class attribute

sorry i'm new to c++ and i feel a little stupid for asking, but i can't get it to work after hours of googling and i can't find what i'm doing wrong
the task is pretty easy:
i want to have employees that have name, age etc (those work)
and i want these employees to be stored inside an array.
so i have:
class Employee {
public:
Employee(string, string, int, int);
string name;
string firstName;
int birthYear;
int usedVacation;
int getAge();
};
and then the list:
class EmployeeList {
public:
int addEmployee(string, string, int, int);
Employee eList[500];
};
what i want is: an object that holds an array of Employees, with methods to add/edit etc them.
so i define the addEmployee method outside the class as follows:
int EmployeeList::addEmployee(string first, string last, int year, int used) {
int i = 0;
for (i; i < this.eList.length; i++) {
if (this.eList[i].deleted == true) {
this.eList[i] = {
firstName: first,
lastName : last,
birthYear : year,
usedVacation : used,
deleted : false };
} else {
this.eList.push({ firstName: first, lastName : last, birthYear : year, usedVacation : used, deleted : false });
}
return i;
}
};
As you probably instantly see, there's a lot wrong with that.
All of the this throw Expression must have class type in VS2015,
also identifier firstName is undefined and identifier lastName is undefined,
but in my eyes, there's nothing wrong with my code.
i'm pretty sure this is a very basic thing i just didn't get yet, but i just can't find out, where the problem is.
I defined methods outside Employee, and this works there without a problem (although with this->, but i tried that and it doesn't work either)
please forgive my lack of skills, i come from javascript :(
this.eList[i] =
this is a pointer, not a reference, so you access the members via this->eList. But 99.99% of the time, it's completely unnecessary. You're inside a member method, so the compiler knows what this is. All you need is eList[i] =.
this.eList.push({
First a brief tutorial with arrays in C++. Employee eList[500] doesn't make an array that can hold 500 Employees, eList is 500 Employees. Forever and always. They are are instantly constructed with the default constructor (or will be once you give Employee a default constructor. This is required if you want to have objects in an array, except in very advanced cases).
this.eList.length
There are three actions you can do with arrays: you can access an element with the [i] syntax, they magically convert to a pointer to the first item very often, and finally, if and only if they're a member of a struct, they can be copied. That's all you can do with them. There is no push, because as I said, it is 500 Employee. There is no length member. Yours is always 500 Employees.
if (this.eList[i].deleted == true)
You gave each Employee 4 members, deleted was not one of them. Instead of keeping track of which Employee objects are unused, the normal thing is to keep all the employees in the first few slots, and give EmployeeList a count to keep track of how many "live" employees there are. The live ones are in indecies 0-count. The employees at count-500 are "deleted". Unfortunately, this does mean that when one is deleted, you have to shift forward all the ones after that.
this.eList[i] = {
firstName: first,
lastName : last,
Employee does not have a lastName. The right way to construct an Employee is via the constructor you declared: Employee(string, string, int, int). So that line should probably be eList[i] = Employee(first, last, year, used);. This creates a new Employee object, and then copies it into that slot of the array. Alternatively, just assign the members one by one.
How about something like:
#include<string>
class Employee
{
public:
std::string firstname;
std::lastname;
int birthyear;
int usedVacation;
void setFirstname(std::string fname){firstname = fname;}
void setLastname(std::string lname){lastname = lname;}
void setBirthyear(int year){birthyear = year;}
void setVacation(int vac){usedVacation = vac;}
};
class EmployeeList
{
public:
int count;
EmployeeList eList[500];
EmployeeList()
{
count = 0;
}
void addEmployee(std::string fname, std::string lname, int year, int vac);
void removeEmployee(std::string fname, std::string lname, int year, int vac);
};
void EmployeeList::addEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count++;
}
void EmployeeList::removeEmployee(std::string fname, std::string lname, int year, int vac)
{
//some stuff goes here
count--;
}
BTW I am not sure where your push, deleted, and length methods came from? You will likely also want to add more methods then what I have written, for example a method to return count. You can also add get methods to the employee class and make your member variables private. I hope this is of some help. Goodluck!

C++ Passing a string into a constructor just passes "" unless explicitly modified

I have this class people
class people {
public:
string name;
int balance;
people(string name, int newb) {
name = name;
balance = newb;
}
};
I thought I had a decent understanding of constructors until I couldn't figure out why this wouldn't work:
for(int i=0; i<users; i++) {
fin>>temp_name; // !!!!!
givers_list.push_back(temp_name, 0);
}
No error, only when I'm debugging I have a vector of users that all have names = "" and balance = 0; I am also 100% sure that temp_name is a string like "David"
Only when I make a temporary object, "cat", construct it, modify it's name, and then push_back does it work right:
for(int i=0; i<users; i++) {
fin>>temp_name; // !!!!!
people cat = people(temp_name, 0);
cat.name = temp_name;
givers_list.push_back(cat);
}
Why didn't it work the first time? I've checked SO and C++ forums and I believe they did what I did. Curiously, I'm sure the issue is regarding a string because when I say givers_list.push_back(people(temp_name, 50));, then the object would correctly have 50 as a balance (name is still "").
My background started with C++ and I've done Java for a long time but returning to C++ is a little alien. Sorry if this is a stupid mistake. I already finished and submitted the program I just don't know why the constructor doesn't work without a stent.
Let me know if this replicates for you.
The problem is that your constructor is incorrect.
Inside:
people(string name, int newb) {
name = name;
balance = newb;
}
name refers to the parameter name, not the class data member name.
To fix this, either qualify name with this, or directly construct name and balance, rather than default-constructing them and then assigning to them:
//Option 1:
people(string name, int newb) {
this->name = name;
balance = newb;
}
//Option 2: Preferred; avoids duplicate initialisation.
people(string name, int newb) :
name(name), balance(newb)
{
}
//Option 3: Optimal; avoids unnecessary copies.
people(string name, int newb) :
name(std::move(name)), balance(newb)
{
}
This line
name = name;
The compiler is using the parameter name for each part of this statement. This means it is assigning the parameter name to itself.
Change it to
this->name = name;

Am I using strcpy_s incorrectly?

I have a constructor for a Person class called "Person" it looks like this:
Person(const char * their_name, const char * email, int day, int month, int year)
: name(0), email_address(0), birthday(day, month, year) {
name = new char [strlen(their_name)+1];
strcpy_s(name, strlen(their_name) +1, their_name);
email_address = new char[strlen(email) + 1];
strcpy_s(email_address, strlen(email) + 1, email);
cout << "\nPerson(...) FIRST CONSTRUCTOR CREATING: "<<name<<"\n";
printOn(cout);
}
I have private variables in this class:
private:
char * name;
char * email_address;
Date birthday;
I think there is an off by 1 error or something in here, because when I test my constructor like this in main:
Person *p1 = new Person("Sarah", "iam#awesome.com", 2,2,1000);
this prints to my console:
Person(...) FIRST CONSTRUCTOR CREATING: Sarah
ààà
I don't understand why it is printing the a's after the constructor runs... Can anyone see the issue?
EDIT: My printOn method
/*print person on output stream o*/
virtual void printOn(ostream & o) const {
//print person
o << "………";
}
and overriden << operator
ostream & operator<<(ostream & ostr, const Person & p) {
p.printOn(ostr);
return ostr;
}
It is not clear what function printOn(cout); does in your constructor. Can it be that it is its output?
As for function strcpy_s then there is no any need to use it. It would be much better to use old function strcpy. In your code you call strlen twice: one time when you allocate memory and the second time when you use strcpy_s.
Your output is using a different text encoding than your editor is. The character that shows as … in your code editor becomes à when you print it out.
In general, don't use high-ASCII characters unless you know how to choose the proper encoding, otherwise things won't work the way you think.

How do I fix this lvalue warning?

My code is:
void main() {
person student[10];
student[0].names[0] = 'C';
student[0].names[1] = 'a';
student[0].names[2] = 'm';
student[0].names[3] = 'i';
student[0].ages = 16;
student[0].sex[0] = 'F';
student[0].sex[1] = 'e';
student[0].sex[2] = 'm';
student[0].sex[3] = 'a';
student[0].sex[4] = 'l';
student[0].sex[5] = 'e';
student[0].month = 8;
student[0].day = 2;
student[0].year = 1993;
}
All of the "student" is underlined saying expression must be a modifiable lvalue. How can i fix this?
person
typedef struct person
{
char names[20][10];
char sex[6][10];
int ages[10];
int month[10];
int day[10];
int year[10];
} person;
Array usage
You say you have:
typedef struct person {
char names[20][10];
char sex[6][10];
int ages[10];
int month[10];
int day[10];
int year[10];
} person;
There's no need for the [10]'s. You already have that in the person student[10] declaration, which is the proper place for the [10]. Remove the extraneous arrays:
typedef struct person {
char name[20];
char sex[6];
int age;
int month;
int day;
int year;
} person;
String handling
Also your strings aren't null-terminated. In C strings need to have an extra '\0' character at the end to indicate where the end of the string is. Your name assignment, for example, should be:
student[0].name[0] = 'C';
student[0].name[1] = 'a';
student[0].name[2] = 'm';
student[0].name[3] = 'i';
student[0].name[4] = '\0';
Actually though, there's an easier way to assign to a string than to do it a character at a time. The strcpy function will copy an entire string in one go:
strcpy(student[0].name, "Cami");
Or, the easiest option of all is to use the string class available in C++. It makes string-handling a whole lot easier than the C way of manipulating character arrays. With the string class your code would look like this:
// Modified struct declaration.
typedef struct person {
std::string name;
std::string sex;
int age;
// ...
} person;
// Modified assignment.
student[0].name = "Cami";
student[0].names[0] has a type of (char *) and cannot be assigned to.
You need to assign to student[0].names[0][0] (or change the definition of names)
Why have you defined names as a 2D array with 10 columns? Do you think you need to do this because you are going to create 10 objects of person?
If that's the case, remember this: when you create the person structure, you just have to define member variables for that one object.
So if your person only has one name, and one sex (which needs 6 characters to define) just use that many because when you create 10 objects of person (person student[10];) you are creating 10 unique copies of the person object! So person[0].name is different from person[1].name.
So let's first define member variables in person for one person:
struct person
{
char names[20];
char sex[6];
int age;
int month;
int day;
int year;
};
And then assign characters to the name as you've detailed in your code.
Now, this is not a great idea because names is uninitialized. So student[0].names[4] is undefined (when it should a null character to terminate it). Have you considered using std::string? You have C++ after all!
Also, you'll notice I've changed the definition of person ('struct person ... instead oftypedef .. `) since this is all you need to do in C++.
Changes:
Consider the following person structure:
#include <string>
struct person
{
std::string person;
std::string sex;
int age;
int month;
int day;
int year;
};
Now, you can assign a string by just:
person student[10];
student[0].name = "Cami";
student[0].sex = "Female";
student[0].age = 16;
student[0].month = 8;
student[0].day = 2;
student[0].year = 1993;
And suppose you want to populate it with a for loop with keyboard input --- it's much easier now:
#include <iostream>
for(int i = 0; i < 10; ++i)
{
std::cin>>student[i].name;
std::cin>>student[i].sex;
std::cin>>student[i].age;
std::cin>>student[i].month;
std::cin>>student[i].day;
std::cin>>student[i].year;
}
You don't need those extra [10]s on each of the fields of the person struct. This makes those fields into arrays themselves. You want, for example, an array of ten persons, each of which has an age. What you have right now is an array of ten persons, each of which has ten ages.
Each element of the student array has its own instance of each of the fields defined in person, so the fields do not themselves have to be arrays.
A non-modifiable lvalue is addressable, but not assignable.
Having said that, are you intending for the struct to be a container for a bunch of different names?
What you really need is an array of struct to create a container for persons. (i.e. struct person p[10];)
If person isn't defined in this file, your are probably not including the proper header file.
Don't forget to add \0 to your strings
If names is a 2d array, shouldnt it be :
names[0][0] = 'J';
names[0][1] = 'i';
names[0][2] = 'm';
names[0][3] = '\0';