I need to add multiple students to the course class but it keeps overwriting the last entry.
I tried to set the address to the object and also using the bracket operator but this causes a memory leak. The problem is with the AddStudent function
class Student {
public:
Student() { name = "unknown"; };
Student(string n) { name = n; };
void Print() { cout << name << endl; };
string GetName() { return name; };
private:
string name;
};
class Course {
public:
Course(int i) {
id=i;
nstudents=0;
capacity=0;
};
void AddStudent(Student s) {
students=&s;
nstudents++;
};
private:
int capacity;
int nstudents;
Student* students;
int id;
};
It only lets me add one student.
"students=&s;" you are taking the address of a copy-by-value variable which becomes dangling after you leave the function. You don't store the students. You have to put a container in it something like std::vector and copy/move your students in it.
So here with a std::vector with copy and move.
class Course {
public:
Course(int i) {
id=i;
};
void AddStudent(const Student& s) {
students.push_back(s);
};
void AddStudent(Student&& s) {
students.push_back(std::move(s));
};
private:
std::vector<Student> students;
int id;
};
You need to allocate memory for your Students array.In your constructor class add : students = new Student[SomeInitialSize] .Also in your add method you need to check if the size of your students is enough to store another student s,and if there is not you need to allocate more memory.
When you add your first student, students is set to point to s inside the AddStudent function. The problem is since s is local to the function, it is destroyed when the function returns. So students is now a dangling pointer pointing to nowhere since the memory it points to is no longer used to store the Student object. In order to keep the memory after the AddStudent function returns, you would have to dynamically allocate it with new, but there are other problems with that.
Let's say you dynamically allocate the memory for the new student. When we add a second student, some more memory will be allocated to store that. That new memory could end up being in a totally different place. Your AddStudent function will set the students pointer to point to the new Student object, but now we've forgotten where the existing student is stored.
So how do we fix this? We could allocate an array of Student objects, leaving extra space for new students. This way, all the students are stored in a contiguous piece of memory, and students will always point to the first object. In our AddStudent function, we would put the new student after the last student we currently have by doing something like students[nstudents] = s, before incrementing nstudents.
The problem with this is that if we exceed the capacity of the array, we would have to allocate a new larger array and copy everything over since we can't expand an existing block of allocated memory. Or you could just make the array fixed sized. But there's a better solution: std::vector.
std::vector is a standard library container that manages the memory for you. You can store your students there and easily add one using push_back. You can learn how to use vectors by asking your instructor, reading a good book, or finding a tutorial online (do note that there are many bad ones out there).
Related
I'm probably going to confuse myself while writing this, sorry in advance:
Is there a way I can access a location in a dynamic array(increment an array pointer) of pointers using the sizeof() the object that is in the array?
For example:
I have an dynamic array of type base class Student populated with derived class objects(Graduate, Undergraduate).
Due to this I can't just step through my array in the normal fashion to display information because the actual objects Graduate and Undergraduate are different sizes than Student. Each array step will move sizeof(Student) when the actual objects are larger.
Depending on the type of student I am doing(this is for Graduate):
Student *student = new Graduate(undergrad, fName, lName, major, idNum, arr2, cSize,
degreeType, thesis);
arr[i] = student;
Where arr was declared: Student *arr = new Student[size];
Using my array I had created this in a for loop:
if (!students[i].getGradStatus()){
handleGraduate(&students[i], i);
step = step + sizeof(Graduate);
}
else if (students[i].getGradStatus()){
handleUndergraduate(&students[i], i);
step = step + sizeof(Undergraduate);
}
I was trying to come up with a way to change the step size. I don't think this will work with a for loop but a while loop may be different. Pretty much I'm trying to go something similar to a file seekg() but manually on an array.
And as I've noticed everyone likes to question the use of dynamic arrays over vectors so let me just say I cannot use vectors on this project(No STL is allowed :( ). And I have to use Polymorphism, thus why I have an array pointer of type Student holding derived class objects.
You can't store different sized objects in an array like that. When you try to copy a derived type like Graduate over a base type like Student you get what's known as slicing because of the differences in object size (parts can get chopped off).
To do this you need to store Student* (pointers to Students.)
class Student
{
std::string name;
public:
Student(const std::string& name): name(name) {}
virtual ~Student() {} // virtual destructor
virtual void handle() = 0; // implementation must override this
std::string get_name() const { return name; }
};
class Graduate
: public Student
{
public:
Graduate(const std::string& name): Student(name) {}
virtual void handle() { std::cout << "Handling Graduate" << '\n'; }
};
class UnderGraduate
: public Student
{
public:
UnderGraduate(const std::string& name): Student(name) {}
virtual void handle() { std::cout << "Handling UnderGraduate" << '\n'; }
};
int main()
{
Student** students = new Student*[3];
students[0] = new Graduate("Wendy");
students[1] = new UnderGraduate("Bob");
students[2] = new Graduate("Rachel");
for(int i = 0; i < 3; ++i)
{
std::cout << students[i]->get_name() << '\n';
students[i]->handle(); // polymorphic function (virtual)
}
delete[] students; // clean up or (better) use a smart pointer
}
Output:
Wendy
Handling Graduate
Bob
Handling UnderGraduate
Rachel
Handling Graduate
Change your array to be an array of Student pointers instead of Student objects.
Student **arr = new Student*[size];
When you do that your objects will live on the heap and the pointer size is always the same, so the issue you are having will go away.
Since you can write that I assume that it's possible to instantiate a Student (non-derived). That's bad, since you're going to store derived objects in the array your students will be sliced (poor students!). To counter that you're doing weird things with the size of the instance types. Just don't.
Make an array of student pointers instead, Student **arr. Pointers have fixed size and they will point to any of the derived types of student so you don't have to worry about their sizes.
Student **student = new Student*[size];
...
student[i] = new Graduate(...);
This should work.
I'm a c++ student and this is my second post here. I'm working on a class whose job is to maintain an array of object pointers. (That way a pointer can be passed in and added to the array, instead of the entire object.)
The array is supposed to be dynamic but I'm running into some errors when I try to dynamically allocate memory for it. The following code produces the error "expression must be a modifiable lvalue" at studArray, as marked.
#ifndef MYCLASS_H
#define MYCLASS_H
#include "Student.h"
class myClass{
private:
Student* studArray[5];
int howMany;
int max;
public:
myClass(){
Student firstOne;
studArray[0] = &firstOne;
howMany=0;
max=5;
}//myClass()
void insertEl( Student* nextEl ){
howMany++;
if(howMany >= max){
Student** tempPt = new Student* [max + 1];
for( int i = 0; i < currentNum; i++){
tempPt[i] = studArray[i];
}
delete [] studArray;
studArray = tempPt; // <-------------------------error
}
studArray[ howMany ] = nextEl;
}//insertEl
};
I tried changing the original Student * array to have no size specified, but that produced the error "incomplete type is not allowed" at studArray.
class myClass{
private:
Student* studArray[]; <------------- error
int howMany;
int max;
What am I doing wrong?
(Thank you for the help!)
For reference I'm using Win 7 64bit, Visual Studio Professional 2012.
Assumptions: Instructor want you to learn the fine art of memory management on your own. So no library containers and no smart pointers. When you have the time, look into how to use the standard containers and the smart pointers. They'll save you a huge amount of time and grief in the future.
First, declaring the Student array.
Student* studArray[5];
You have the crux of it right here
Student** tempPt = new Student* [max + 1];
So try
Student** studArray;
And then hack your constructor to allocate the storage
myClass()
{
max = 5;
studArray = new Student*[max]; /* For improved safety, read up on
exceptions, catch and handle the
out of memory exception */
howMany = 0;
} //myClass()
I recommend adding a destructor to handle the clean up and put back your array.
virtual ~myClass()
{
delete[] studArray;
}
Check with the assignment notes or the instructor to see who is responsible maintaining the Students. You may have to revisit the destructor to delete all of the Students before deleting studArray
Additional note:
myClass()
{
Student firstOne;
studArray[0] = &firstOne;
howMany=0;
max=5;
}//myClass()
Student firstOne; is a temporary variable. firstOne only exists between the closest enclosing {} braces. This is called Scope. Using any value outside of it's scope will have unpredictable results and most likely crash the program. Frankly, that's if you are lucky. The program may limp on for some indeterminate time and crash later.
The assignment studArray[0] = &firstOne; is dangerous because by the time anyone tries to use studArray[0], the data at which it points will not be valid. firstOnedoes not exist outside the constructor. If you want firstOne to go on living, you will have to define it as a pointer, create it with new, and delete it when it is no longer needed. In this case I don't think you ever need it.
Another suggestion is to double the size of studArray rather then simply adding one when it is full. This way you don't have to reallocate the storage and copy all of the existing students to the new storage as often.
A better method is to have a std::vector of Student:
std::vector<Student *> maleStuds;
std::vector<Student *> femaleStuds;
Or, if you must use arrays:
const unsigned int MAXIMUM_STUDENTS = 5;
Student * students[MAXIMUM_STUDENTS];
For dynamic:
Student * * students = new Student * [MAXIMUM_STUDENTS];
I have two classes, PersonnelLists and Employee. I create an instance of PersonnelLists in my main, like so:
int main() {
PersonnelLists example; //Make a personnel list
...
}
PersonnelLists uses a constructor with member initialisation of a list of employees, the number of employees, and the size of the array:
PersonnelLists::PersonnelLists(): List(new Employee[SIZE]), numEmployees(0), arraySize(SIZE){
}
This results in some null empty employees being created (I think?):
Employee::Employee(): employeeNumber(0), name(NULL), department(NULL) {
}
It is at this line that I get an invalid null pointer error.
I am new with C++, fresh off the boat from Java programming. I'm still a novice with pointers, so I'm not quite sure what I'm doing wrong here.
UPDATE:
As requested, here is the class definition of Employee:
#include <iostream>
class Employee {
public:
Employee(); //constructor
Employee(std::string name, std::string deparment);
void Print() const; //Print this employee's details
void setEmployeeNo(int employeeNum);
private:
int employeeNumber;
std::string name;
std::string department;
};
In Java, new Employee[SIZE] creates an array of null references.
In C++, new Employee[SIZE] creates an array of default-constructed instances of Employee. Your default constructor tries to set name and department to NULL. Attempting to initialize a std::string to NULL would give the error you describe.
There's no "null" string in C++, but you could default-construct name and department, which would set them to empty strings:
Employee::Employee(): employeeNumber(0), name(), department() {
Finally, if List can contain a variable number of elements, I would recommend that you use std::vector<Employee> (which is similar to ArrayList<Employee> in Java).
If name and department are std::strings (or a similar string type), then initializing them with NULL (a null character pointer) is invalid.
If I guessed right, you should default-initialize them instead, as:
Employee::Employee(): employeeNumber(0), name(), department() {
}
But we really can't tell without seeing the class definition of Employee.
As others have pointed out, you should use a std::vector instead of an array. That allows you to only
have valid Employee objects in your "list".
I don't know what the actual definitions of your classes are, so it's kind of hard to identify your problem.
But an option in modern C++ of doing that is to use a std::vector<Employee> data member inside PersonnelList class. std::vector can grow dynamically at runtime, using its push_back() method, e.g.
#include <vector> // for std::vector
class Employee
{
....
};
class PersonnelList
{
public:
PersonnelList()
{
// Nothing to do - vector is initialized empty
}
// Get current employee count
size_t Count() const
{
return m_employees.size();
}
// Add a new employee to the personnel
void AddEmployee(const Employee& newEmployee)
{
m_employees.push_back(newEmployee);
}
private:
std::vector<Employee> m_employees;
};
No need to use raw pointers or something similar: robust RAII STL container classes make your code simpler.
Have a homework assignment in which I'm supposed to create a vector of pointers to objects
Later on down the load, I'll be using inheritance/polymorphism to extend the class to include fees for two-day delivery, next day air, etc. However, that is not my concern right now. The final goal of the current program is to just print out every object's content in the vector (name & address) and find it's shipping cost (weight*cost).
My Trouble is not with the logic, I'm just confused on few points related to objects/pointers/vectors in general. But first my code. I basically cut out everything that does not mater right now, int main, will have user input, but right now I hard-coded two examples.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Package {
public:
Package(); //default constructor
Package(string d_name, string d_add, string d_zip, string d_city, string d_state, double c, double w);
double calculateCost(double, double);
~Package();
private:
string dest_name;
string dest_address;
string dest_zip;
string dest_city;
string dest_state;
double weight;
double cost;
};
Package::Package()
{
cout<<"Constucting Package Object with default values: "<<endl;
string dest_name="";
string dest_address="";
string dest_zip="";
string dest_city="";
string dest_state="";
double weight=0;
double cost=0;
}
Package::Package(string d_name, string d_add, string d_zip, string d_city, string d_state, string r_name, string r_add, string r_zip, string r_city, string r_state, double w, double c){
cout<<"Constucting Package Object with user defined values: "<<endl;
string dest_name=d_name;
string dest_address=d_add;
string dest_zip=d_zip;
string dest_city=d_city;
string dest_state=d_state;
double weight=w;
double cost=c;
}
Package::~Package()
{
cout<<"Deconstructing Package Object!"<<endl;
delete Package;
}
double Package::calculateCost(double x, double y){
return x+y;
}
int main(){
double cost=0;
vector<Package*> shipment;
cout<<"Enter Shipping Cost: "<<endl;
cin>>cost;
shipment.push_back(new Package("tom r","123 thunder road", "90210", "Red Bank", "NJ", cost, 10.5));
shipment.push_back(new Package ("Harry Potter","10 Madison Avenue", "55555", "New York", "NY", cost, 32.3));
return 0;
}
So my questions are:
I'm told I have to use a vector
of Object Pointers, not Objects.
Why? My assignment calls for it
specifically, but I'm also told it
won't work otherwise.
Where should I be creating this
vector?
Should it be part of my Package
Class? How do I go about adding
objects into it then?
Do I need a copy constructor? Why?
What's the proper way to deconstruct
my vector of object pointers?
Any help would be appreciated. I've searched for a lot of related articles on here and I realize that my program will have memory leaks. Using one of the specialized ptrs from boost:: will not be available for me to use. Right now, I'm more concerned with getting the foundation of my program built. That way I can actually get down to the functionality I need to create.
Thanks.
A vector of pointers can be reused for storing objects of sub-classes:
class Person
{
public:
virtual const std::string& to_string () = 0;
virtual ~Person () { }
};
class Student : public Person
{
const std::string& to_string ()
{
// return name + grade
}
};
class Employee : public Person
{
const std::string& to_string ()
{
// return name + salary
}
};
std::vector<Person*> persons;
person.push_back (new Student (name, grade));
person.push_back (new Employee (name, salary));
person[0]->to_string (); // name + grade
person[1]->to_string (); // name + salary
Ideally the vector should be wrapped up in a class. This makes memory management easier. It also facilitates changing the support data structure (here an std::vector) without breaking existing client code:
class PersonList
{
public:
Person* AddStudent (const std::string& name, int grade)
{
Person* p = new Student (name, grade);
persons.push_back (p);
return p;
}
Person* AddEmployee (const std::string& name, double salary)
{
Person* p = new Employee (name, salary);
persons.push_back (p);
return p;
}
~PersonList ()
{
size_t sz = persons.size ();
for (size_t i = 0; i < sz; ++i)
delete persons[i];
}
private
std::vector<Person*> persons;
};
So we can re-write our code as:
{
PersonList persons;
Person* student = persons.AddStudent (name, grade);
Person* employee = persons.AddEmployee (name, salary);
student.to_string ();
employee.to_string ();
} // The memory allocated for the Person objects will be deleted when
// `persons` go out of scope here.
Getting familiar with the Rule of Three will help you decide when to add a copy constructor to a class. Also read about const correctness.
Question 1:
You mentioned inheritance. Since inherited objects often need more bytes of storage, they don't fit into the place of a base object. If you try to put them in, you get a base object instead. This is called object slicing.
Question 2:
Design first, before you write code. There are a bunch of possible solutions.
For a start you can keep it in main(), but later you will be forced to make a class like PackageContainer for holding your objects.
Question 3 + 4:
You need a copy constructor, an assignment operator= and a destructor, when a class object owns dynamically allocated objects (the Rule of the Big Three). So a PackageContainer will probably need them.
You create objects dynamically using new Object(..). You are responsible for destroying them and for giving their memory back to the system immediately before your vector of pointers is destroyed:
for (size_t i = 0; i < shipment.size(); ++i)
{
delete shipment[i];
}
Since working with naked pointers to dynamically allocated objects is not safe, consider using
std::vector<tr1::shared_ptr<Package> > shipment;
instead or
std::vector<std::shared_ptr<Package> > shipment;
if your compiler understands C++0x. The shared_ptr handles freeing memory for you: It implements the Rule of the Big Three for one object pointer. It should be used in production quality code.
But try to get it right with naked pointers also. I think that's what your homework assignment is about.
I'm told I have to use a vector of Object Pointers, not Objects. Why? My assignment calls for it specifically, but I'm also told it won't work otherwise.
Usually, one would avoid using vector of objects to avoid the problem of Object Slicing. To make polymorphism work You have to use some kind of pointers. I am not sure of how the classes in your assignment are aligned but probably you might have Inheritance there somewhere and hence if vector is storing objects of Base class and you insert objects of Derived class in it then it would cause the derived class members to slice off.
The Best solution will be to use a smart pointer instead of a Raw pointer. The STL has an auto_ptr, but that cannot be used in a standard container.Boost smart pointers would be a best solution but as you already said you can't use Boost So in your case you can use your compiler's implementation of smart pointers, which comes in TR1 namespace,remember though that there is some disagreement on the namespace for TR1 functions (Visual C++ puts them in std::, while GCC puts them in std::tr1::).
Where should I be creating this vector? Should it be part of my Package Class? How do I go about adding objects into it then?
Your example code already has an example of adding a pointer to Package class in a vector. In a nutshell you will dynamically allocate pointers to Package and then add them to the vector.
Do I need a copy constructor? Why?
The copy constructor generated by the compiler does member-wise copying. Sometimes that is not sufficient. For example:
class MyClass {
public:
MyClass( const char* str );
~MyClass();
private:
char* str;
};
MyClass::MyClass( const char* str2 )
{
str = new char[srtlen( str2 ) + 1 ];
strcpy( str, str2 );
}
Class::~Class()
{
delete[] str;
}
In this case member-wise copying of str member will not duplicate the buffer (only the pointer will be copied(shallow copy)), so the first to be destroyed copy sharing the buffer will call delete[] successfully and the second will run into Undefined Behavior. You need deep copying copy constructor (and assignment operator as well) in such a scenario.
When to use a custom copy constructor is best defined by the Rule Of Three:
Whenever you are writing either one of Destructor, Copy Constructor or Copy Assignment Operator, you probably need to write the other two.
What's the proper way to deconstruct my vector of object pointers?
You will have to explicitly call delete on each contained pointer to delete the content it is pointing to.
vector::erase
Removes from the vector container and calls its destructor but If the contained object is a pointer it doesnt take ownership of destroying it.
Check out this answer here to know how to corrctly delete a vector of pointer to objects.
I have a class symbol_table that has a vector of objects of another class row_st.also I have an enter method where inserts objects of row_st with a passed name into the vector of desired symbol_table.but when I call the enter to enter objects with name :
a;b;c;Iwill get the following result: a,b,c;b,c;c.the first element of vector gets the name of all the entered objects. and the second element also gets the name of the later entries.
class row_st
{
public:
char* name;
type_u type;//int:0,flaot:1;char:2,bool:3,array:
int offset;
symbol_table *next;
symbol_table *current;
};
class symbol_table
{
public:
vector <row_st *> row;
int type;
int header;
int starting_stmt;
int index;
int i;
symbol_table *previous;
symbol_table(){ header=0;
previous=0; index=0;i=0;starting_stmt=0;}
};
and here it is the enter method:
int enter(symbol_table *table,char* name,type_u type){
row_st *t=new row_st;
t->name=name;
t->type=type;
t->offset=table->index;
t->current=table;
table->index++;
t->next=0;
table->row.push_back(t);
table->header +=1;
return table->row.size()-1;
}
the push_backed elements all points to the same address.the new call makes the same row_st every time it is called.what should I do?
You can't use character pointers like that - you need to allocate storage to them. But as you are using C++, you should remove them and replace them with instances of the std::string class, which will manage storage for you.
As Neil Butterworth's answer suggest, the trouble is probably not with this code, but the place where you call it. Using character pointers does not make it impossible to make things work, just harder.
The problem in this case is definitely not with push_back. If you posted the method where you call this code it might be possible to see exactly what goes wrong.