Dynamic string array without using vector - c++

I am working on an assignment where we have to create a "student" object with a "course" member array that is dynamic so the user can enter from one course to as many as they'd like in the array. I've been trying this every which way and just cannot get the array to resize and accept new elements. Here is my code as it stands now:
cout << "Enter student name > ";
cin >> name;
Student firstStudent(name);
cout << endl << "Enter \"done\" when you are complete";
cout << endl << "Enter a new course : ";
cin >> course;
while (course != "done") {
numCourses++;
firstStudent.addElement(numCourses, course);//makes the array bigger
cout << endl << "Enter a new course : ";
cin >> course;
}//end while
member variables and constructor:
class Student
{
private:
string name;//name of student
string *dynArray = new string[1];//array
public:
Student::Student(string name)
{
this->name = name;
this->numC = 1;//initialized to one to start with
}
resizing and adding an element to the array:
void Student::addElement(int numCourses, string &course) {//DYNAMIC ARRAY
if (numCourses > this->numC) {//checks if there are more courses than there is room in the array
this->numC = numCourses; //this changes the member variable amount
dynArray[numCourses] = course;//adds new course to array
string *temp = new string[numC];//create bigger temp array
temp = dynArray;//write original to temp
delete[]dynArray; //this tells it that it's an array
dynArray = temp;//make dynarray point to temp
}
else {
dynArray[numCourses] = course;//adds new course to array
}
}//end addElement
Even if I manage to get rid of compile errors, it will often come up with runtime errors. I'm sure it has to do with pointers/copying arrays but I'm just learning and haven't yet mastered these concepts.
Edit: dynArray[numCourses] = course; creates a char array. ie if course = "one", dynArray[0] = "o", dynArray[ 1] = "n", etc. Does anyone know how to keep this from happening?
screenshot

The first line that that doesn't look quite correct is this one:
dynArray[numCourses] = course;
Before it is checked that numCurses > numC, which means that dynArray[numCourses] accesses memory out of bounds (the bounds are 0 to numC - 1.
The other thing that strikes me as interesting is that the addElement method takes numCourses as an argument. I would expect that it should behave similar to std::vector<T>::push_back. Are you sure this argument is necessary?
The last thing I want to comment on is that the values from dynArray are not copied. Have a look at std::copy_n on how to do the copy.

I'm not sure if this would be considered cheating, but here's a break down of how std::vector is implemented. You could use that as a reference and just pick out the parts you need.
http://codefreakr.com/how-is-c-stl-implemented-internally/

Related

Increasing class pointer array

In my code, I want to add one student info into my class pointer array and the array size will increase each time a new student is added. Here is my code:
My header file:
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray);}
My Class definition file:
void Student::AddStudent(Student *tempStudent[], int countStudent, int sizeOfArray)
{
for (int i = countStudent; i < sizeOfArray; i++)
{
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent[i]->studentID;
cout << "Please enter student name: ";
cin >> tempStudent[i]->studentName;
}
}
My Main.cpp file
int *totalStudent = new int;
*totalStudent = 1;
int i, j, countStudent = 0;
int sizeOfArray = *totalStudent;
Student *newStudent[*totalStudent];
//Each time a new student is added, I will allocate a new memory for the array element, then add student Info using function.
for (i = countStudent; i < *totalStudent; i++)
{
newStudent[i] = new Student;
newStudent[i]->AddStudent(newStudent, countStudent, sizeOfArray);
countStudent++;
*totalStudent++;
}
When I run my code, I get an undefined reference error, so I do not know If I am able to increase my array or not. I intend to use C++ syntax so I use new and delete. Thank you for your help.
P.S: Here is my new code and it runs great, the only missing is the studentID for the first element in array.
In my main class:
int numStudent = 0;
int i, j, countStudent = 1;
Student *newStudent = new Student[countStudent];
AddStudent(newStudent, countStudent, numStudent);
My Student.h
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
};
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent);
and My Student.cpp
Student AddStudent(Student *newStudent, int &countStudent, int &numStudent)
{
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
cin >> tempStudent.studentID;
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
newStudent[numStudent] = tempStudent;
numStudent++;
if (numStudent == countStudent)
{
Student *newStudentSize = new Student[countStudent + 1];
for (int i = 0; i < numStudent; i++)
{
newStudentSize[i] = newStudent[i];
}
delete []newStudent;
return *newStudentSize;
countStudent += 1;
}
}
Running this code will give me the following result:
StudentID: 11
StudentName: Dat
StudentID: 23
StudentName: Michael
Printing:
StudentID: 0
StudentName: Dat
StudentID: 23
StudentName: Michael
While it doesn't make sense to increase the array for each new student (it's inefficient) here's one way you can do it (I didn't even try to make your code compile because it has a number of issues and is unnecessarily complicated). Note that tempStudent (in the code snippet below) doesn't even have to be created using new. This solution stores Student objects in the students array (although it's easy to modify it to store Student object pointers instead). That said, usually, you'll just want to create an array of large enough size to accomodate all students (just set studentCount to some appropriate number and not 1 like in the example below).
class Student{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student(){};
};
int main(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//Student *tempStudent = new Student();
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount, studentCount*2);
studentCount *= 2;
}
}//end while
//print the students info
for(int i=0;i<numStudents;i++){
Student st = students[i];
cout << st.studentID << " " << st.studentName << std::endl;
}
//deallocate the students array or if you place this in the main like you did, the program will exit immediately so there's no need to deallocate
return 0;
}
Student * ReallocateStudents(Student* st, int oldSize, int newSize){
Student * newStudents = new Student[newSize];
//copy the existing values from the old array to the new one
for(int i=0;i<oldSize;i++){
newStudents[i] = st[i];
}
delete [] st; //delete the old array
return newStudents;
}
UPDATE:
Since you don't want to do everthing in the main(), just create a free AddStudents function and do everything there. Alternatively, you can create a
static function inside the Student class. It makes no sense to create AddStudent as a member of Student because that would require you to use an instance of Student to add a new instance, which makes for poor design (not to mention technical issues etc).
int main(){
// some code here
Students * allStudents = AddStudents();
//print students
}//end main
Students * AddStudents(){
int studentCount=1;
Student * students = new Student[studentCount];
int numStudents=0;
bool done=false;
char finished='N';
while (!done){
//create a Student on the stack
Student tempStudent;
cout << "Please enter student id (4 digits only): ";
//No input checking is done here
cin >> tempStudent.studentID;
No input checking is done here
cout << "Please enter student name: ";
cin >> tempStudent.studentName;
students[numStudents] = tempStudent;
numStudents++;
cout << "Stop entering students: Y or N";
cin >> finished;
done = finished=='Y' or finished=='y' ? true : false;
if(numStudents==studentCount){
students = ReallocateStudents(students, studentCount,
studentCount*2);
studentCount *= 2;
}
}//end while
return students;
}
This is simple and easy to both understand and maintain so I suggest using this approach.
addStudent does not do anything with the Student object it belongs to. So there is no need to put it in the 'Student' class. (Or you could rather rewrite it so it does something with the student object it belongs to). And it currently does not "add" anything, so the name is confusing.
What is technically wrong with it depends on what you want it to do. Currently it initializes student objects expected to already exist and pointed to by an array, from a specific array index, to the end of the array. That could well be a useful function, if that is what you want it to do. But you then must call it correctly with an array of pointers that point to valid Student objects, which you currently do not.
Currently in main you have a loop that initializes pointers in an array. And each time you initialize a pointer, you call AddStudent(..). The problem is that 'AddStudent()' tries to initialize ALL the student pointed to by your array.
This has two major problems (In addition to all the other problems with your loop).
Each time you create a new student, all your existing students will be
initialized again with new input from std::cin. (So for n students, you will
try to do n*n initializations)
While the loop in main is running, not all pointers in your array points
to existing Student objects. This may result in important data being
overwritten, a program crash or something totally different and unexpected.
You should sit back and reevaluate how you want to do things. Trying to fix single bugs in your existing code, one after another, will just create more bugs.
Just a hint to get you started:
class Student
{
public:
int studentID;
char studentName[20];
int currentEnrollment;
Student();
void init_from_cin();
};
And in your class definition file:
void Student::init_from_cin()
{
cout << "Please enter student id (4 digits only): ";
cin >> studentID;
cout << "Please enter student name: ";
cin >> studentName;
}
If you create a new Student like this:
Student *new_student = new Student;
new_student->init_from_cin();
Then after calling init_from_cin(), the Student object pointed to by new_student should be initialized.
How to create and initialize multiple Student objects in a loop, is left as exercise for the reader. But when you do it, you should understand what your lower and upper bounds of your loop are supposed to be. And you should also understand why moving the upper bound further away while your loop is running is a bad idea.
And never forget that sane programming languages start array indexing with 0.

Adding object to array of pointers to objects

I have problem with adding object to array of pointers of objects. Here's a function I'll be talking about:
void add(Car **cars, int number) {
Car *fresh = new Car;
fresh = new Car;
cout << "Enter the name of your car." << endl;
cin >> fresh->name;
cout << "Enter max velocity of your car." << endl;
cin >> fresh->maxV;
cout << "Enter weight of your car." << endl;
cin >> fresh->weight;
delete[number-1] cars;
cars[number-1] = fresh; // here's something wrong
}
I allocated memory for number-of cars in array of pointers **cars and then I try to add new object fresh at the end of array, firstly deleting memory in last index of array of pointers of objects and then passing reference to last index of array, but I get an error. I tried to solve problem myself, by I didn't found similiar topic in web. I hope somebody will guide me how to solve it. Thanks for help.
No need of delete statement. The code would be like this if you already allocated memory for 'cars'.
Car *fresh = new Car;
cars[number-1] = fresh; //number here should be index starting from 1 not number of Car objects
The cars would be initialised like following before calling method add().
Car ** cars = new (Car *)[number];
if you need to delete the allocated memory for cars[number-1] use
delete cars[number-1];
Otherwise, you don't need to call new twice to allocate fresh.
So, the following should work
void add(Car **cars, int number) {
Car *fresh = new Car;
cout << "Enter the name of your car." << endl;
cin >> fresh->name;
delete cars[number-1];
cars[number-1] = fresh;
}
and you need to make sure that number is the size of your cars array.
It could be better if you use
std::vector<Car> cars;
So you could do the next:
cars.push_back(fresh);
And that's all. Sorry about my english

looping with cin inside the loop, thus storing different char*s in an array (c++)

I'm writing a basic program in C++ that creates a student database (name, major and GPA for each student). The user enters how many students will be in the database, and then enters the data items for each student one by one.
I made a loop that executes "x student" number of times and adds the students one by one to an array of Students by calling cin.getline for each data item and then creating an instance of Student with these items which it adds to the array in the next available place.
Name and major are char* variables (they cannot be strings for this assignment).
I'm having (I assume) pointer-based problems, because after putting a watch on the array of students, I realized that as soon as a new student is added to the array, every previous student in the array is set to the same data as the new student. I think it's because the names of the variables are always the same , since theyre in a loop, and I think that when I create a new Student object it keeps being put in the same address, thus changing all previous items in the array.
I have tried deleting the address of newStudent, and I've tried setting it to NULL after it adds a new student, but my program either breaks or the name and major in newStudent become lines of gibberish instead of deleting their content.
Here's the pertinent bits from main.cpp: (I removed the parts where I was trying to delete things and set things to NULL because they were making it worse and I don't know if I was on the right track). this is just the first part of main. it calls other functions later that I wrote, but I tested those and they all work fine. This is the part that's making it all break.
#include<iostream>
#include "student.h"
using namespace std;
int main(){
int numMajors = 0; // keeps track of the total number of Majors
int currAdded = 0; // keeps track of how many students have been added THUS FAR
const int MAX_MAJORS = 100; // max number of majors allowed
const int MAX_LENGTH = 25; // max length of character array
const int TABLE_SIZE = 100; // max table size
int numStudents = 0; // total number of students being added
char* name = new char[MAX_LENGTH]; // cin>>name
char* major = new char[MAX_LENGTH]; // cin>>major
float GPA = 0; // cin>>gpa
//Get Data
cout << "--Student Database Data Entry--" << endl << endl;
cout << "Enter the number of students in registry: ";
cin >> numStudents;
cout << endl << "Enter data for each student: " << endl;
Student table[TABLE_SIZE];
char* majors[MAX_MAJORS];
for(int i = 0; i < numStudents; i++){
cout << " Student " << (i+1) << " : " << endl;
cout << " Last Name: ";
// need to use getline to cin multiple words, so using
// cin.ignore() to eat the trailing \n from cin>>numStudents
cin.ignore();
cin.getline(name, MAX_LENGTH);
cout << " Major: ";
cin.getline(major, MAX_LENGTH);
cout << " GPA: ";
cin >> GPA;
Student* newStudent = new Student(name, major, GPA);
table[i] = *newStudent;
currAdded++;
and here is my constructors and destructor for Student (in student.cpp)
Student::Student() :
name(NULL),
major(NULL),
GPA(0)
{
}
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = name;
this->major = major;
this->GPA = GPA;
}
Student::~Student()
{
if(name)
delete [] name;
if(major)
delete [] major;
}
Student& Student::operator=(Student& student)
{
this->name = student.name;
this->major = student.major;
this->GPA = student.GPA;
return *this;
}
note: when my destructor is called, it also fills the name and major variables it's supposed to be deleting with long strings of gibberish characters, but that's a whole other problem. I commented it out for a while to temporarily get around this problem but I realize that's not a long term solution (the two are probably related)
If there's any more code you'd like to see I would be happy to post it, I'm trying to keep this as short as I can and I'm not sure if more is necessary.
Thank you so much for your insight
You have to copy the strings passed instead of just assigning the pointers passed.
Try this:
#include <cstring> // for using strlen() and strcpy()
Student::Student(char* name, char* major, float GPA) :
name(NULL),
major(NULL),
GPA(0)
{
this->name = new char[strlen(name) + 1]; // +1 for terminating null-character
strcpy(this->name, name);
this->major = new char[strlen(major) + 1];
strcpy(this->major, major);
this->GPA = GPA;
}

Logic Error Assigning and Printing Arrays

void opt3()
{
cout << "ENTER STUDENT NAME>";
cin >> Assigned[Col][Row];//Get New Assignment of Student Name at appropriate Col and Row
Col++;
cout << "ENTER COURSE ID>";
cin >> Assigned[Col][Row];//Get new Assignement of class at appropriate Col and Row
Row++;
return;
}
void opt7(int& Col, int& Row)
{
cin.clear();
cin.ignore();
int x,y,z;
string CouC;
cout << "ENTER COURSE ID>";
getline(cin,CouC,'\n');
bool Ans;
do
{
for (int i=0;i<Row;i++)
{
for (int j=0;j<Col;j++)
{
x = j;
y = i;
Ans = CouC.compare(Assigned[j][i]);
if(Ans)
z=j;
//Compares until it finds the right course ID woop and then sets it to arr$
}
}
}while (Ans == false);
for (y=0;y<10;y++)
{
cout << Assigned[z][y];//Print out the students in the course
}
This is what my problem section of code looks like. I am having difficulty storing the students in the desired class and then printing them out based on the chosen class. I am trying to compare my array Assigned[][] to the appropriate user input and I can`t seem to figure it out. I currently have no compile errors only logic errors. My output when this is run looks weird. It usually prints a random student name with the course name entered.
Help would be appreciated, Thanks!
It looks like the meaning of the two-dimensional array Assigned needs to be defined clearly.
It could be a boolean matrix of students and courses, where the value would indicate if a student is taking a course, or
It could be a list of student and course pairs.
The function opt3() that fills Assigned does something weird: it takes a student and a course, and creates a new column for the student and a new row for the course. So this function seems to use Assigned as a matrix, but it adds both a new student and a new course at the same time. Also the values of this matrix are a mix of student and course IDs, and it has many unused values. This does not make much sense.
If the purpose of the function is to input which student takes which course, it should, depending on the meaning of Assigned, either
search for the student column and course row, and set the value to true, or
add a new student and course pair to the list.

Trying to figure out the mistake I made with pointers (C++)

I'm a newbie to C++, currently working on a project on dynamic allocation in my C++ class. I can't for the life of me figure out where I went wrong with pointers in this program. I have three different .cpp files that I'm working with, we're supposed to make a rudimentary playlist program.
Please don't give me tips on any of the other messy code I've got going, I'm simply trying to figure out the pointers:
(Playlist class, contains a dynamic array of songs)
Playlist::Playlist (int s) {
size = s;
list = new Song[size];
}
Playlist::~Playlist() {
delete [] list;
}
void Playlist::Add(Song * s) {
size++;
Song * newlist = new Song[size];
for(int i = 0; i < size - 1; i++) {
newlist[i] = list[i];
}
delete [] list;
newlist[size] = *s;
list = newlist;
}
(Menu program)
switch (choice) {
case 'A': {
cout << "\nEnter a song title to add: ";
cin.getline(tempTitle,36);
cout << "Enter the artist's name: ";
cin.getline(tempArtist,20);
cout << "Enter the category of the song ([P]op, [R]ock, [A]lternative, [C]ountry, [H]ip Hop, or Parod[Y$
cin >> catStorage;
tempCat = StyleChoice(catStorage);
cout << "Enter the size of the song in kilobytes: ";
cin >> tempSize;
Song * tempS = new Song;
tempS->Set(tempTitle, tempArtist, tempCat, tempSize);
mainlist.Add(tempS);
delete tempS;
break;
}
I get an error whenever I run the Add() function and then exit the menu program (I have an "X" condition that ends the while loop that keeps the menu going). Thanks! Let me know if you need more information on what any of the functions do.
Change:
newlist[size] = *s;
To:
newlist[size-1] = *s;
There are two possible issues:
Your assignment is always out of bounds. To access the last element of the array, you'll always have to use newlist[size - 1] rather than newlist[size] since the very first element is always at index 0 in C/C++.
Since you don't verify that you've got an old list before trying to delete it with delete [] list; you'll have to make sure that your pointer is initially set to NULL or there is some initial (valid) pointer assigned to it that indeed can be deleted.