C++ How to properly access dynamic array elements using class method - c++

I've created a class Student which contains a dynamic array. I've filled the first two items with the constructor. Every method I've tried to use to access/print those two elements from the main get a read/access violation and crashes. I've added a cout in the constructor that shows the elements ARE filled and exist. I've included two failed methods in the main: A void function that attempts to send first element to cout, and a method that accepts an int for the desired index. Both have been commented out to allow a test run showing elements are created and printed by the constructor.
Header:
#ifndef STUDENT_H
#define STUDENT_H
#include <string>
#include <iostream>
#define ARRAY_MAX 15
using namespace std;
class Student
{
private:
string firstName, lastName;
unsigned int ID, numItems = 0;
typedef string* StringPtr;
StringPtr items;
public:
int capacity = 15;
Student();
Student(const string fName, const string lName, const unsigned int id);
string getfName() const;
string getlName() const;
void getItem(int num);
string getItemB(int num) const;
unsigned int getID() const;
};
#endif // STUDENT_H
Definitions:
#include "student.h"
using namespace std;
Student::Student()
{
}
Student::Student(const string fName, const string lName, const unsigned int id)
{
firstName = fName;
lastName = lName;
ID = id;
StringPtr items = new string[capacity];
numItems = 0;
items[0] = "stuff";
items[1] = "things";
cout << items[0] << endl << items[1] << endl;
}
string Student::getfName() const
{
return firstName;
}
string Student::getlName() const
{
return lastName;
}
void Student::getItem(int num)
{
cout << items[0] << endl;
}
string Student::getItemB(int num) const
{
return items[num];
}
unsigned int Student::getID() const
{
return ID;
}
Main:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include "student.h"
using namespace std;
int main()
{
Student stu;
string str;
stu = Student("John", "Smith", 1200);
cout << stu.getfName() << " " << stu.getlName() << endl;
//stu.getItem(0);
//cout << stu.getItemB(0);
system("pause");
// Quit without error
return 0;
}

Solution
Change
StringPtr items = new string[capacity];
into
items = new string[capacity];
TL;DR
In
Student::Student(const string fName, const string lName, const unsigned int id)
{
firstName = fName;
lastName = lName;
ID = id;
StringPtr items = new string[capacity];
numItems = 0;
items[0] = "stuff";
items[1] = "things";
cout << items[0] << endl << items[1] << endl;
}
The line
StringPtr items = new string[capacity];
creates a new Automatic (local) variable items and initializes it rather than the intended private member variable StringPtr items;. This is commonly known as Variable Shadowing. The local items goes out of scope at the end of the function leaking the allocated memory and the member items is never initialized leading to stu.getItem(0); accessing an uninitialized pointer and triggering the crash.
void Student::getItem(int num)
{
cout << items[0] << endl; // items points Crom knows where, so items[0] is invalid.
}
This crash is quite fortunate. Accessing an unitintialized pointer results in Undefined Behaviour which means it could do anything from looking like it works to causing the universe to explode.
The next problem you have to deal with is observing The Rule of Three.
The best way to deal with this is std::vector
std::vector<string> items;
std::vector is Rule of Three (Rule of Five, actually) compliant so that you don't have to be.
If std::vector is not allowed, you need to implement a copy constructor
Student::Student(const Student & src):
firstName(src.firstName), lastName(src.lastName),
ID(src.ID), numItems(src.numItems),
items(new string[capacity])
{
for (int i = 0; i < src.numItems; i++)
{
items[i] = src.items[i];
}
}
and an assignment operator (Taking advantage of the Copy and Swap Idiom for simplicity)
Student & Student::operator=(Student src)
{
swap(*this,src);
return *this;
}
Writing swap I'll leave up to you.

Related

C++ Unable to assign Character Array Variable to String Variable

I am in need of assistance.
I am trying to create a vector structure where the string contents inside of a char array get automatically assign to another STRING variable within the same structure.
After countless hours, I have not been able to figure it out. When I used function such as string(). It doesn't copy anything. I can't seem to assign char variable to string variable. Please advise.
#include <iostream>
#include <ctime>
#include <fstream>
#include <string>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
const int NAME_SIZE = 25;
struct tableInfo
{
char name2[NAME_SIZE];
string name = str(name2); // I need name string to equal name2 character variable
string info;
string link;
};
vector<tableInfo> table(6);
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs);
int main()
{
cin.getline(table[0].name2, 51);
cin.getline(table[1].name2, 51);
cin.getline(table[2].name2, 51);
cin.getline(table[3].name2, 51);
cin.getline(table[4].name2, 51);
sort(table.begin(), table.end(), compareByWord);
cout << table[0].name << endl;
cout << table[1].name << endl;
cout << table[2].name << endl;
cout << table[3].name << endl;
cout << table[4].name << endl;
}
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs)
{
unsigned int length = lhs.name.length();
if (rhs.name.length() < length)
length = rhs.name.length();
int sameLetters = 0;
for (unsigned int i = 0; i < length; ++i)
{
if (sameLetters == length)
return false;
if (tolower(lhs.name[i]) == tolower(rhs.name[i]))
{
++sameLetters;
continue;
}
return(lhs.name[i] < rhs.name[i]);
return(lhs.name[i] < rhs.name[i]);
}
return false;
}
If you want something to happen in a structure (or class), consider making sure it happens by using the constructor. You will need to change your vector so you can no longer default construct the struture, but that's ok.
This works, by enforcing the two fields contain identical data (not withstanding 25 no being long enough):
const int NAME_SIZE = 25;
struct tableInfo
{
tableInfo(char * whatever); //Make it so
char name2[NAME_SIZE];
string name;
string info;
string link;
};
tableInfo::tableInfo(char *whatever)
: name(whatever)
{
strncpy(name2, whatever, NAME_SIZE); //Now they match - unless whatever was too big
}
//vector<tableInfo> table(6);//no longer ok with the non-default constructor
bool compareByWord(const tableInfo &lhs, const tableInfo &rhs);
int main()
{
vector<tableInfo> table;
for (int i = 0; i < 5; ++i)
{
char name2[NAME_SIZE];
cin.getline(name2, 51); //Do you really mean 51?
// It's smaller than NAME_SIZE
table.emplace_back(name2);
}
sort(table.begin(), table.end(), compareByWord);
cout << table[0].name << endl;
cout << table[1].name << endl;
cout << table[2].name << endl;
cout << table[3].name << endl;
cout << table[4].name << endl;
}
How about doing like this:
struct tableInfo
{
char name2[NAME_SIZE];
string name;
string info;
string link;
tableInfo(const char* in_name)
{
memset(name2, 0, NAME_SIZE);
strncpy(name2, in_name, NAME_SIZE - 1);
name = string(name2);
}
};
How to use:
char tmp[NAME_SIZE];
cin.getline(tmp, 51);
table[0] = tableInfo(tmp);
I argue that you don't even need an array in the struct. Just use std::string. getline supports reading into a std::string and you can access individual characters using str[]. If you need to get a pointer to the data you can use str.c_str().
If you really need to use an array in the struct you can create a member function to return a std::string:
struct tableName {
char name[NAME_SIZE];
std::string info;
std::string link;
std::string nameString() const { return name; }
};

Add string elements to Dynamic Array

Parsing a file and need to add students to a struct vector using an array for student names specific to that course line.
In my course.h file:
struct Course {
std::string name;
int enrollment;
int maxEnrollment;
std::string* students; ///< array of student names
Course(std::string courseName, int maxEnrollmentPermitted);
bool enroll(std::string studentName);
void print(std::ostream& output);
};
In my course.cpp file:
bool Course::enroll(std::string studentName) {
this->students = new std::string[studentName];
if (this->enrollment < this->maxEnrollment) {
this->enrollment++;
return true;
}
else {
return false;
In my source file:
void processEnrollmentRequests(istream& enrollmentRequestsFile, vector<Course>& courses) {
// Read the requests, one at a time, serving each one
string courseName;
enrollmentRequestsFile >> courseName;
while (enrollmentRequestsFile) {
enrollmentRequestsFile >> ws;
string studentName;
getline(enrollmentRequestsFile, studentName);
int pos = search(courses, courseName);
if (pos >= 0) {
// Found a matching course
bool enrolled = courses[pos].enroll(studentName);
if (enrolled) {
cout << studentName << " has enrolled in " << courseName << endl;
}
else {
// course is full
cout << studentName << " cannot enroll in " << courseName << endl;
}
}
else {
// course does not exist
cout << studentName << " cannot enroll in " << courseName << endl;
}
enrollmentRequestsFile >> courseName;
}
}
}
}
I cant seem to add the gathered studentName to the array using this->students = new std::string[studentName]. Getting an error that says must have integral or enumeration type.
new SomeThing[size] is used to declare array. It makes no sense to use a string as the size.
Assuming the size of students is limited to maxEnrollment, you can use this:
if (this->enrollment < this->maxEnrollment) {
this->students[this->enrollment++] = studentName;
return true;
}
else {
return false;
For the sake of completeness, the allocation of students is not the only problem. Given that the code you posted also uses std::vector<Course>, and Course does not follow the rule of 3, using it in a std::vector is highly likely to cause memory corruption, leaks, etc.
Given that you state that students must remain a pointer, the complete fix is to write Course in this manner:
#include <string>
#include <algorithm>
struct Course {
std::string name;
int enrollment;
int maxEnrollment;
std::string* students; ///< array of student names
Course(std::string courseName, int maxEnrollmentPermitted);
bool enroll(std::string studentName);
void print(std::ostream& output);
Course(const Course& rhs);
Course& operator =(const Course& rhs);
~Course();
};
Course::Course(const Course& rhs) : name(rhs.name),
enrollment(rhs.enrollment),
maxEnrollment(rhs.maxEnrollment),
students(new std::string[rhs.maxEnrollment])
{
for (int i = 0; i < maxEnrollment; ++i)
students[i] = rhs.students[i];
}
Course& Course::operator= (const Course& rhs)
{
Course temp(rhs);
std::swap(temp.students, students);
std::swap(temp.maxEnrollment, maxEnrollment);
std::swap(temp.enrollment, enrollment);
std::swap(temp.name, name);
return *this;
}
Course::~Course() { delete [] students; }
Course::Course(std::string courseName, int maxEnrollmentPermitted) :
name(courseName),
enrollment(0),
maxEnrollment(maxEnrollmentPermitted),
students(new std::string[maxEnrollmentPermitted])
{}
Why all of this code? Well, in the code you posted in your question, you are using a std::vector<Course>. The Course class as written could not be safely used in a vector, due to Course having incorrect copy semantics. Thus your error you're getting may have a lot to do with code you stated wasn't yours (the vector<Course>).
The adjustments to Course above now makes Course safe to be used in a vector since the copy semantics (copy constructor, assignment operator, and destructor) have now been implemented to handle the dynamically allocated students member.
Note that absolutely none of this code would be necessary if students were simply a std::vector<std::string> instead of std::string *.
For more reading:
What is the rule of 3?
What is the copy / swap idiom?

Difficulty with using an overloaded addition operator

So here is my program. I have to make an object of type Student, then have the Student "check out" an item. And I am using an overloaded addition operator to make the user check out that item.
main.cpp:
#include <iostream>
#include "Student.h"
using namespace std;
int main() {
Student s(54000, "JOHN", "DOE");
cout << "main:" << endl << (s + "Frisbee") << endl << endl;
system("pause");
return 0;
}
I defined all my class defintions in the header file to try and keep this program minimal and simplified.
Student.h:
#ifndef STUDENT_H
#define STUDENT_H
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
class Student {
public:
string firstName;
string lastName;
int id;
int itemsCheckedOut;
int size;
string *array;
Student(int id = 0, string firstName = "", string lastName = "") {
Student::firstName = firstName;
Student::lastName = lastName;
Student::id = id;
itemsCheckedOut = 0;
size = 10;
array = new string[size];
}
Student(const Student &other) {
itemsCheckedOut = other.itemsCheckedOut;
array = new string[itemsCheckedOut];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = other.array[i];
}
}
~Student() {
delete[] array;
array = NULL;
}
Student &operator=(const Student &rhs) {
if (this != &rhs) {
firstName = rhs.firstName;
lastName = rhs.lastName;
id = rhs.id;
itemsCheckedOut = rhs.itemsCheckedOut;
delete[] array;
array = new string[size];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = rhs.array[i];
}
}
return *this;
}
void CheckOut(const string &item) {
array[itemsCheckedOut] = item;
itemsCheckedOut++;
}
friend ostream &operator<<(ostream &output, const Student &student) {
output << student.id << " " << student.firstName << " " << student.lastName << endl;
if (student.itemsCheckedOut != 0) {
output << student.itemsCheckedOut;
for (int i = 0; i < student.itemsCheckedOut; i++) {
output << " " << student.array[i] << endl;
}
}
else {
output << 0;
}
return output;
}
const Student operator+(const string &item) {
Student s;
s = *this;
s.CheckOut(item);
cout << "class:" << endl << s << endl << endl;
return s;
}
};
#endif
output:
class:
54000 JOHN DOE
1 Frisbee
main:
-858993460
1 Frisbee
As you can see, from the main, its outputting the wrong thing. Instead of outputting the id followed by two spaces then the first name and last name, it outputs the number: -858993460. This has gotta be some sort of memory leak issue or something, but I'm pretty sure my copy constructor, overloaded assignment operator, and deconstructor are all defined correctly, but you can take a look at them.
I would appreciate any help at all as I am getting pretty desperate here. Thanks.
Your actual operator+ looks correct. But there are bugs in your copy-constructor and assignment-operator that would cause it to malfunction:
The copy-constructor does not set size, id, or the names.
The copy-constructor should allocate [size] items, not [itemsCheckedOut].
The assignment operator does not copy size.
The assignment operator allocates a new array whose dimension is the old size, probably causing an immediate buffer overflow.
The checkOut function does not check that it doesn't write beyond size. It needs to detect this case and either reject the checkout, or allocate more space. (I mentioned this last time you posted a question about this project)
It calls copy constructor:
Student(const Student &other) {
itemsCheckedOut = other.itemsCheckedOut;
array = new string[itemsCheckedOut];
for (int i = 0; i < itemsCheckedOut; i++) {
array[i] = other.array[i];
}
}
but you forget to copy all Student's fields in its body.
You override default copy constructor, so you should manually execute all data copying, as in assignment operator.
You should replace your string* array with a std::vector. It will handle the memory management for you, make your code far easier and less error prone than the manual memory management you are currently using. You can reserve an initial size of 10 if you are worried about it doing allocations when adding items (although with such small data sizes that shouldn't ever be a problem).

Storing Objects in an Array of a second Object

I have to create a small console application in C++ which will do the following:
Create class Subject which has next attributes: name of the subject, number of students and array of students who are attending that subject. After that createa class Student which has name and surname of the student as attributes. In Main file count how many duplicate names there are in each Subject.
I have few problems here. First one is that I don't know how to initialize an array in my Subject.h file. Second is how to actually put Student objects into Subject objects and compare the names in the end.
What I'd like output to look like:
Duplicate names in subjectA are: Michael.
Duplicate names in subjectB are: Nicholas, John.
Where subjectA and subjectB should be called C++ and C.
Here is my code so far (I googled for past hour or two about this problem of mine but I just couldn't find a proper answer/example).
NOTE: I'm including all these files for clarification.
Subject.h
#include <string>
#include <iostream>
using namespace std;
/*
* I should also have an array named `arrayOfStudents`
* which should store all students who are attending
* that Subject.
*/
class Subject
{
public:
Subject();
Subject(Subject &subject);
Subject(string nameOfSubject, int numberOfStudents);
~Subject();
const string getNameOfSubject();
const int getNumberOfStudents();
void setNameOfSubject(string nameOfSubject);
void setNumberOfStudents(int numberOfStudents);
void print();
private:
string nameOfSubject;
int numberOfStudents;
};
Subject.cpp
#include <iostream>
#include "Subject.h"
using namespace std;
Subject::Subject()
{
}
Subject::Subject(string nameOfSubject, int numberOfStudents)
{
this->nameOfSubject = nameOfSubject;
this->numberOfStudents = numberOfStudents;
}
Subject::Subject(Subject &Subject) : nameOfSubject(Subject.getNameOfSubject()), numberOfStudents(Subject.getNumberOfStudents())
{
}
Subject::~Subject()
{
cout << "Object is destroyed" << endl;
}
const string Subject::getNameOfSubject()
{
return nameOfSubject;
}
const int Subject::getNumberOfStudents()
{
return numberOfStudents;
}
void Subject::setNameOfSubject(string nameOfSubject)
{
nameOfSubject = this->nameOfSubject;
}
void Subject::setNumberOfStudents(int numberOfStudents)
{
numberOfStudents = this->numberOfStudents;
}
void Subject::print()
{
cout << "Subject: " << nameOfSubject << " :: Number of students: " << numberOfStudents << endl;
}
Student.h
#include <string>
#include <iostream>
using namespace std;
class Student
{
public:
Student();
Student(Student &student);
Student(string name, string surname);
~Student();
const string getName();
const string getSurname();
void setName(string name);
void setSurname(string surname);
void print();
private:
string name;
string surname;
};
Student.cpp
#include <iostream>
#include "Student.h"
using namespace std;
Student::Student()
{
}
Student::Student(string name, string surname)
{
this->name = name;
this->surname = surname;
}
Student::Student(Student &student) : name(student.getName()), surname(student.getSurname())
{
}
Student::~Student()
{
cout << "Object is destroyed" << endl;
}
const string Student::getName()
{
return name;
}
const string Student::getSurname()
{
return surname;
}
void Student::setName(string name)
{
name = this->name;
}
void Student::setSurname(string surname)
{
surname = this->surname;
}
void Student::print()
{
cout << "Student: " << name << " " << surname << endl;
}
Main.cpp
#include <iostream>
#include "Subject.h"
#include "Student.h"
using namespace std;
int main()
{
/*
* First three students should attend first Subject
* while other four the second Subject.
* Also note that only names matter and not surnames.
*/
Student stA("Michael", "Doe");
Student stB("Michael", "Doe");
Student stC("Thomas", "Doe");
Student stD("Nicholas", "Doe");
Student stE("Nicholas", "Doe");
Student stF("John", "Doe");
Student stG("John", "Doe");
Subject subjectA("C++", 3);
Subject subjectB("C", 4);
return 0;
}
1) Get an array of Students into your Subject object: you probably want to use vectors instead of arrays here:
in subject.h add
#include "Student.h"
public:
void addStudent(Student student);
private:
std::vector<Student> students_;
in subject.cpp add
void Subject::addStudent(Student student)
{
this->students_.push_back(student);
}
If you want to extract the student list somehow later, you need to write a function to access it (or make it public).
2) For finding duplicates, look here
Checking for duplicates in a vector
You have to pay attention: the Student objects are in your subject object, not the student names. You have to extract them first and e.g. put them in a vector.
Your task definition sais you should have an array of Students attribute of the Subject class, but i don't see this in your Subject class definition.
And maybe an add Student method and then iterate over the array.

Unable to access member function in a vector of objects

I am trying to simply create a vector of objects, add an object to it, and then display some variables in that object in the vector by accessing the function "getter's". Code compiles but it shows nothing for functions that return string and a garbage number for the ones that return double. I am sure it is something simple....can you help (assignment due in a couple of hours :S)?
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Employee
{
string fName, lName;
double weeklyIncome, grossSales, bonus;
public:
Employee(){};
Employee(string fn, string ln, double sales)
{
fName = fn;
lName = ln;
grossSales = sales;
bonus = sales * 0.09;
weeklyIncome = 200;
}
double getNetIncome() {return bonus + weeklyIncome;}
string getfName() {return fName;}
double getgrossSales() {return grossSales;}
~Employee(){};
};
int main()
{
vector<Employee> salespeople(15);
Employee e1("John", "Smith", 5000);
salespeople.push_back(e1);
cout << salespeople[0].getNetIncome();
cout << salespeople[0].getgrossSales();
cout << salespeople[0].getfName();
system("pause");
return 0;
}
vector<Employee> salespeople(15); // This creates a vector of 15 elements
Employee e1("John", "Smith", 5000);
salespeople.push_back(e1); // Now you have added the 16-th element
cout << salespeople[0].getNetIncome(); // The first element is still the default
// constructed element.
cout << salespeople[0].getgrossSales();
cout << salespeople[0].getfName();
You can fix the problem by using
vector<Employee> salespeople; // Create an empty vector and then
// add items to it.
or by changing
salespeople.push_back(e1);
to
salespeople[0] = e1;
Suggestion for improvement
Change the default constructor to initialize the double variables to sane values.
Employee() : weeklyIncome(0.0), grossSales(0.0) bonus(0.0) {};