How do I fix this lvalue warning? - c++

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';

Related

I need to define a casting constructor - a parameter that denotes the name of the document

If it's not difficult, please tell me - I have a "Document" class, with following fields:
char * name
char * subject
char * author
int page
int date
int time
In this class, I need to define a casting constructor - a parameter that denotes the name of the document.
Should my constructor take a string object and translate it into an enchant array?
Did I do the right thing ?
Document::Document( const string str ) { ctor type casting - accept an object of type string (document name)
name = new char[ str.length() + 1 ]; // then using c_str - returns an array of char type, terminated with zero
strcpy ( name , str.c_str() ); // After converting string to char - write it in the "name" field of the Document class
topic = new char[1]; // For the rest of the char fields - allocate one byte of memory
author = new char[1];
pages = date = time = 0; // Fields of type int - initialized with zeros
}
Q: Should my constructor take a string object and translate it into an char array (assuming an enchant array is actually a char array)?
A: Your code looks more or less correct, although you probably shouldn't use a char array in your class but just have std::strings. This will save you a lot of trouble.
Q: Did I do the right thing ?
A: Yes ans no, the code you show is more or less correct, but the general approach using arrays of char rather than std::strings looks wrong to me.
It'd do simply this:
class Document
{
std::string name;
std::string subject;
std::string author;
int pages;
int date;
int time;
public:
Document(const std::string & str);
};
Document::Document(const std::string & str)
{
name = str;
pages = date = time = 0;
}
or better:
class Document
{
std::string name;
std::string subject;
std::string author;
int pages = 0;
int date = 0;
int time = 0;
public:
Document(const std::string & str);
};
Document::Document(const std::string & str)
: name(str)
{
}
Example of usage:
int main()
{
Document foo("bar");
...
}

Typedef gives error "no storage class or specifier"

I was programming a project the other day. I'm a beginner to C++, so I don't know that much. Here's the code:
typedef struct notes{
int keyint;
char name;
std::string filename;
int orn;
note;
note a0;
a0.keyint = 0;
a0.name = 'a';
a0.filename = "a0.wav";
a0.orn = 1;
This is very confusing.
Here is the corrected form:
struct Note
{
int keyint;
char name;
std::string filename;
int orn;
};
Note a0;
a0.keyint = 0;
a0.name = 'a';
a0.filename = "a0.wav";
a0.orn = 1;
In my style, I prefer to capitalize structure and class names. This helps distinguish class names from variable names.
Note: The code should be placed inside of a function or method. C++ doesn't allow code to be executed outside of a function. (If your an advance C++ guru, you'll know there are ways around requiring code in functions or methods).

I have a class object as a member of another class, how do I initialize as a safe empty state?

I have 2 classes, ISBN, Order. I have an ISBN object as a data member of my Order class and I am having issues with the Order constructor to place the ISBN object in a safe empty state.
My Order.h
#include <iostream>
using namespace std;
class ISBN;
class Order {
int ordered;
int delivered;
ISBN * book;
bool empty;
public:
Order();
Order(const ISBN & isbn);
};
My ISBN.h
#include <iostream>
using namespace std;
class ISBNPrefix;
class ISBN {
char isbnNum[13];
char area[6];
char publisher[8];
char title[7];
char checkDigit[1];
bool emptycheck;
bool registered;
public:
ISBN();
ISBN(const char * str, const ISBNPrefix& list);
}
In my Order constructor I tried this code:
Order::Order() {
ordered = 0;
delivered = 0;
empty = true;
*book->ISBN();
/*
(*book).isbnNum[0] = '\0';
book.area[0] = '\0';
book.publisher[0] = '\0';
book.title[0] = '\0';
book.checkDigit[0] = '\0';
book.emptycheck = true;
book.registered = false; */
}
And variations of it, but I get errors like: "type name is not allowed" "expression must have pointer type" etc...Anyone know what my issue is?
You almost certainly don't want a pointer here, just an ISBN object as a data member:
ISBN book;
This will automatically be initialised using its default constructor; you don't need to do anything. If you want to initialise it using the other constructor (with arguments), then you'll need to do that in the initialiser list:
Order::Order() : book(some_string, some_list)
{
// body of constructor
}
You are having problems because you have declared book as an ISBN*. Therefore your posted line *book->ISBN(); is trying to dereference a null and then call the blank constructor.
If you want to manually allocate book, then you should then use this pattern:
Order::Order() {
ordered = 0;
delivered = 0;
empty = true;
book = new ISBN();
}
Note this will require Order's destructor to call delete on its book member.
You can automatically allocate and delete book as an ISBN by making it a class member, and not a pointer. For that, use this declaration:
class Order {
ISBN book;
... // your other members
}
This will automatically allocate and automatically deallocate an ISBN object member whenever class Order is instatiated and destroyed respectively. No additional steps necessary.

a special Person class in 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

How to manage objects in a fixed array?

This if for my homework.
I have a class called Student that takes 3 parameters (id, name, class) and I want to store each student in an array called Roster (which can only have 7 students).
The user will provides input to add or remove students. Thus, I have to manage the array by creating or deleting students. So if the user specify the student ID, I have to remove him for the array.
I tried to use a fixed array, but I'm struggling to make it works. Is there a better way to implement this?
I must not use a vector or any STL container.
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include <string>
static const int SIZE = 7;
class Student {
private:
int student_id;
std::string name;
std::string classification;
public:
Student(int, std::string, std::string); // constructor; initialize the list to be empty
~Student();
void print();
};
#endif
student.cpp
#include <iostream>
#include <string>
#include "student.h"
#define PROMPT "class> "
using namespace std;
Student::Student(int a, string b, string c){
student_id = a;
name = b;
classification = c;
}
Student::~Student(){
//delete Student
}
void Student::print(){
cout<<"Enrolled:"<<endl;
cout<<student_id<<"-"<<name<<"-"<<classification<<endl;
}
main.cpp
#include <iostream>
#include <string>
//#include <sstream>
#include "student.h"
#define PROMPT "class> "
using namespace std;
//**** Implement Error Handling ****\\
enum errorType {
UNKNOWN_ERROR,
INPUT_ERROR,
HANDLER,
NUM_ERRORS
};
// error messages
string errorMessage[NUM_ERRORS] = {
"Unknown Error\n",
"Input Error\n",
};
// error handler
void handleError(errorType err) {
if(err > 0 && err < NUM_ERRORS)
cout<< "Error: "<< errorMessage[err];
else cout<< "Error: "<< errorMessage[UNKNOWN_ERROR];
}
//**** END Error Handling ****\\
void enroll(Student newStudent){
cout<<"test";
Student roster[SIZE];
for(int i=0;i<SIZE;i++){
newStudent->roster[i];
}
}
void handleInput() {
int id; string n, c;
cin>>id>>n>>c;
Student newStudent(id,n,c);
newStudent.print();
enroll(newStudent);
//cout<<"hello3"<<endl;
return;
}
int main() {
//Student newStudent; /* <-- why doesn't this work?!*/
string input = "";
bool finished = false;
cout<<PROMPT; // prompt the user
while(!finished) {
if(input!="") cout<<PROMPT;
cin>>input;
if(input=="enroll") {
cout<<PROMPT<<"Enroll student:"<<endl;
handleInput();
}
else if(input=="drop") {
cout<<PROMPT<<"Enter ID:"<<endl;
}
else if(input=="roster") {
cout<<"This will print formatted list of students"<<endl;
}
else if(input=="quit") {
finished=true;
}
else handleError(errorType(1));
}
}
Since it is a homework, I'd like to point out some mistakes you did because it is important to understand what you are doing in the first place.
You must not program by coincidence, but by trying to understand exactly what's going on. By doing that you will become better and better and the answers should fall in place.
What you've done
So, from what you are describing, the array is fixed. Thus it is a good idea to use a constant as you did (SIZE).
However, as we can see below you a declaring an array of size SIZE in the function. By doing that, your array is like a temporary variable, because its scope is inside the function. Each time you call this function, the array will be declared again and then deleted at the exit. So it should be declared outside.
void enroll(Student newStudent)
{
cout<<"test";
Student roster[SIZE]; // Here 'roster' will be available only inside the function.
for(int i=0;i<SIZE;i++)
{
newStudent->roster[i]; // Here there is few mistakes see my explanation below*
}
}
If we look at this part:
newStudent->roster[i];
First of all, the arrow '->' is used with pointers. The dot '.' is used with objects. In both case, it does the same thing, access to public members of Student.
Since you passed
void enroll(Student newStudent)
you should use '.' instead.
newStudent.SomeOfYourMembers;
If the parameter was a pointer to a Student
void enroll(Student *newStudent)
Then, you'd have to use the arrow '->' like you did.
Back to the original statement:
newStudent->roster[i];
This means, you want to access to 'roster' array at position 'i' inside your Student object (newStudent). As you can see in your code, roster is not declared inside Student (and should not be since you want an array of Students), so that won't work.
Guidelines
As I mentionned, your array should be outside the function, so at a higher scope.
Then, if you need an array of student, basically, 'roster[i]' will give you access to the student 'i'. Thus, if you want to print the student, you would do something like that:
roster[i].print();
This would be valid because 'print()' is defined as public.
In order to store a student inside the array, you can do something like:
roster[i] = new Student(0 /* id*/, "name", "classification");
But don't forget, each time you use new, you have to balance it with a delete. And if you are creating the student like this in a loop, you will have to clean them the same way:
for(int i = 0; i < SIZE; ++i)
{
delete roster[i];
}
Good luck!
Don't hesitate if there is there anything that I could clarify. I hope this helps!
Edit: In reply to your first comment.
Concerning the roster array
No, it is not mandatory to create a class roster you could declare roster in the main.cpp.
The key concept is that by defining
Student roster[SIZE];
the array will contains objects of type Student.
What roster[i].print() means is that you are printing one of the Student of that array, in fact the one at position 'i'.
Concerning the print() function
What is powerfull with Object Oriented language, each object will have the same print() function. So, you do not need to convert the array to string.
However, if you want a string to be printed out (or returned) you can write the code inside the print() function that will do this job.
The advantage of this, is that if further on you need to change your array in some ways, your print() function will always work.
Concerning the Delete
When you are doing something like this on an array that contains objects:
delete roster[i];
It will delete the object at the position 'i'. Thus, the destructor of that Student 'i' will be called. If your object Student would contains other object, you would have to delete them in the destructor.
Further notices
Since ID is an input that you are storing into a string, you will have to convert the ID to the same type of the student_id, which is a int. Then you can always write a loop for each student and check their ID to delete the proper one.
Concerning the container, a fixed array might not be the best to achieve this job. You might want to look the LinkedList concept.
It doesn't make much sense for enroll to be a member function, so
I'd wrap the roster into a class to get automatic clean up of my
pointers.
#include <cstddef>
struct Student {};
class Roster
{
private:
static const size_t size = 7;
// non-copyable
Roster(const Roster&);
Roster& operator=(const Roster&);
public:
Roster() {
for(unsigned i = 0; i < size; ++i) {
roster_[i] = NULL;
}
}
~Roster() {
for(unsigned i = 0; i < size; ++i) {
delete roster_[i];
}
}
// enroll by copy
bool enroll(const Student& s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = new Student(s);
return true;
}
}
// out of space
return false;
}
// enroll by taking ownership
bool enroll(Student* s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = s;
return true;
}
}
// out of space
return false;
}
private:
// data
Student* roster_[size];
};
int main()
{
Roster r;
Student s;
r.enroll(s);
Student* sp = new Student();
r.enroll(sp);
return 0;
}
What about this?
Student * roster[2];
roster[0] = new Student(5,"first","2A");
roster[1] = new Student(2,"Second","5B");
Ps:
Enrol and Size shouldn't be members of the student class.
Print should ideally be externalized and a ToString function should be added instead.
You should use the inline constructor initialization instead:
Student(int a,string b,string c):id(a),name(b),class(c){}
You've used the keyword class as a variable name of type string. You shouldn't do that. Does it even compile like that?
enroll should have two arguments: void enroll( Student enrollee, Student Roster[]). You should probably change the name of Roster to roster because it's not a class and typically class names are capitalized.
If your array will only ever have 7 students then you could use some sentinel value to mark that the current student as an invalid student. Perhaps the id will be -1 to mark this. It means basically that you need some way to keep track of which spots in the array you can still use. If you don't do this then declaring an array of Students will get you an array of students with garbage member variables. You wouldn't be able to tell which students are real ones and which are just place holders for when someone new enrolls in the class. I would create a default constructor of Student and initialize its member variables like this:
id=-1;
name="";
name_of_class="";
I changed the name of your string class to avoid confusion.
After all that, enroll would look something like this:
void Student::enroll( Student enrolee, Student roster[]){
//search through roster to check for the first student with an
//id of -1
//if there are no students with id of -1, produce an error message
//that the class is full
//overwrite the student with id of -1 with the id, name, and
//name_of_class of enrollee
}
Although I'm not sure what exactly string class is there for. Does it store what class the Student is in? Is it their year in school like Freshman, Sophomore?
If you're suppose to use dynamic allocation of roster, though, it's a different story, but you said it will only ever have seven students.