So I've declared an array of pointers that look something like
Items* _items[ARRSIZE];
Basically with the goal of using them as an array of objects (one for meat, one for produce) which is dynamically decided on run time. I'm calling the following function in my constructor and I've identified it as the reason I keep segfaulting before the main function.
void Inventory::loadRecs(){
datafile.open(_filename);
int i = 0;
char c;
//create fileif it doesnt exist
if(datafile.fail()){
datafile.clear();
datafile.close();
datafile.open(_filename, ios::out);
datafile.close();
}
else{
//read from file
while(!datafile.eof()){
if(_items[i] != nullptr){
delete _items[i];
}
c = datafile.get();
if(c == 'P'){
_items[i] = new Produce;
}
if (c == 'M'){
_items[i] = new Meat;
}
datafile.ignore();
_items[i]->load(datafile);
i++;
datafile.ignore(); //ignore endl
}
_noOfItems = i;
datafile.close();
}
}
The text file I'm reading from is fairly straight forward reading something like
P,123,carrots,0.66,[NEWLINE]
The first character identifies what kind of product it is (meat or produce) and the rest of the line is read in with the load function.
My Inventory class looks something like this:
class Inventory{
char _filename[256];
Item* _items[5];
std::fstream datafile;
int _noOfItems;
}
And the constructor just initializes everything and calls loadsRecs (which is where I get my segfault from)
I'm willing to bet that you aren't initializing your array of pointers and so your check against nullptr is failing, and you are calling delete on a garbage pointer resulting in undefined behavior.
Unless you have a constructor that you omitted from your code, the pointers are default initialized resulting in indeterminate values.
Since in the code you have shown, you are using new Produce; and new Meat;, I'll assume you have written new Inventory; or Inventory i;. If instead, you included the parenthesis (or braces in C++11) like new Produce(); or Inventory i{};, you would get value initialization which would do zero initialization of your pointers. This would result in the behavior that you seemingly expect.
I have a class Student and the dynamically created array of students:
class Student{
public:
void create(){
system("cls");
cout << "First name: ";
cin >> fn;
cout << "Last name: ";
cin >> ln;
cout << "How many subjects? ";
cin >> sub_number;
subjects = new string[sub_number];
for(int i = 0; i < sub_number; i++){
cout << "Subject name: ";
cin >> subject[i];
}
}
//This just display the student with a bunch of couts
friend ostream& operator<<(ostream&, const Student&);
~Student(){ delete[] subjects; }
private:
string fn;
string ln;
string* subjects;
int sub_number;
};
I need to increase the size of this array by 1 and add a new student to it.
When I tried to do it by simply:
void main(){
int stu_number = 0;
cout << "How many students? "
cin >> stu_number;
Student* students = new Student[stu_number];
//creating/updating initial students using user input
for(int i = 0; i < stu_number; i++){
students[i].create();
}
//displaying all students
for(int i = 0; i < stu_number; i++){
cout << students[i];
}
//trying to add a student
add_new_student(students, stu_number);
//displaying all students again
for(int i = 0; i < stu_number; i++){
cout << students[i];
}
}
And the add_new_student function:
void add_new_student(Student* students, int& stu_number){
Student* temp = new Student[stu_number++];
for (int i = 0; i < stu_number - 1; i++){
temp[i] = students[i];
}
temp[stu_number - 1].create();
delete[] students;
students = temp;
}
I am getting memory violation errors and Unhandled exceptions: Access violation writing location 0xABABABAB inside the add_new_student function.
What will be the correct way of doing this?
Is Student class missing some important components?
I can't use std::vector or a std::list as this is a school assignment:)
To increase the size of the array, you basically have to:
Create a new array with a larger size
Copy over the values from the old smaller array to the new larger one
Delete the old smaller array
That being said, if you are able to I would recommend switching to std::vector since it has utility methods that will resize for you.
The problem is that your Student class does not follow the "rule of 3":
What is The Rule of Three?
If your class has a user-defined destructor, then it should also provide a user-defined copy constructor and assignment operator. What is happening now is that when this line is performed:
temp[i] = student[i];
you are invoking the assignment operator. However, your Student object, when assigned, will just copy the pointer value from one object to the other. What happens when the destructor for either of these Student objects is called? The destructor will delete the same pointer value twice, thus an error.
Here is a slimmed down version of your current Student class.
#include <string>
class Student
{
std::string* subjects;
std::string fn;
std::string ln;
int sub_number;
public:
Student(size_t numSubjects=5) : subjects(new std::string[numSubjects]),
sub_number(numSubjects){}
~Student() { delete [] subjects; }
};
// Test code
int main()
{
Student s1(10);
Student s2 = s1;
Student s3;
s3 = s1;
}
If you run this program, you will see that there are problems with memory leaks and double deletion errors, as shown here: http://ideone.com/MQhpcK
All I'm doing is copying Student objects, and the issue appears.
To fix the problem, you must add and implement these functions in your Student class:
Student(const Student& rhs); // copy constructor
Student& operator=(const Student& rhs); // assignment operator
Once you implement these functions, test the program above to ensure it truly does make the copies correctly, without memory leaks and segmentation faults/access violation errors. When done, your original issue of the copying causing the access violation should be resolved.
Your problem is caused by the missing assignment operator. In the assignment within the for loop, there is a flat copy of the subjects array which is deleted when the original students array is deleted. After that, there is a so-called dangling pointer in first Student object.
The assignment operator has the job to safely (and semantically correct) copy all elements from one object to another.
Additionally: It would be really interesting (informative) to add some trace output as to get a detailed understanding what's going on here.
Whatever create is called for[1], there must be something wrong with the default constructor[2] of your Student class: it seems to miss initializing important elements that are used within create.
[1] BTW: what is the purpose of create?
[2] Is there a default constructor? What C++ standard is used?
For future visitors
It turns out I didn't have a copy assignment operator defined in my custom class, and therefore the compiler defaulted to "copy the object's pointer" behavior.
If you're confused about what a copy assignment operator is, just like I was, this resource may help you figure out what a "copy assignment operator" looks like in C++.
The original problem prompt is below. (Links to the original source code will expire in a month; sorry!)
I'm working on a console application which simulates a bookstore, but keep on getting a _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) error message in my program during execution. After doing some searching around (and some frustrating debugging), I've come to the conclusion that my object's destructor is getting called twice. I hope the code snippets below will make it a little clearer what I mean. (Clicking on the file names will open a pastie with the relevant code for easier reading/decluttering.)
bookdata.h
#ifndef BOOKDATA_H
#define BOOKDATA_H
class bookData {
private:
char* bookTitle;
char* isbn;
char* author;
char* publisher;
char* dateAdded;
int qtyOnHand;
double wholesale;
double retail;
public:
bookData();
bookData(char* title, char* isbn, char* author, char* publisher, char* date, int qty, double wholesale, double retail);
bookData(bookData& book); // Meant to be called during memberwise assignment
~bookData();
// Various setter & getter funcs
};
#endif
bookData.cpp
#include "globals.h"
#include "bookData.h"
using namespace std;
// Variables in caps are const ints defined in globals.h
bookData::bookData() {
bookTitle = new char[TITLE_LENGTH];
isbn = new char[ISBN_LENGTH];
author = new char[AUTHOR_LENGTH];
publisher = new char[PUBLISHER_LENGTH];
dateAdded = new char[DATE_LENGTH];
qtyOnHand = 0;
wholesale = 0;
retail = 0;
char emptyTitle[2];
emptyTitle[0] = '\0';
setTitle(emptyTitle);
}
// Other constructors are overloaded version of bookData & copy data;
// See example setter function below destructor
bookData::~bookData() {
if (bookTitle)
delete [] bookTitle;
else
return;
delete [] isbn;
delete [] author;
delete [] publisher;
delete [] dateAdded;
}
// Setter functions are of this form (excl. ints & doubles)
void bookData::setTitle(const char* input) {
for (int len = 0; len < TITLE_LENGTH - 1; len++) {
*(bookTitle + len) = *(input + len);
if (*(input + len) == '\0')
break;
else if (len == TITLE_LENGTH - 2)
*(bookTitle + ++len) = '\0';
}
}
// Getter functions are of this form (excl. ints & doubles)
const char* bookData::getTitle() { return bookTitle; }
reports.cpp (the file that's calling the destructor twice)
void repQty() {
// Again, variables in all caps are defined in globals.h if you don't see
// their declaration
bookData bookArray[MAX_RECORDS];
// Global function which populates bookArray from a datafile
bookData* books = getBooks(bookArray);
// Some code to find the memory address of the first and last book in the records
bookData* HEAD = books;
// Keep advancing until books no longer points to a non-empty bookData object
// "Empty" defined as book's bookTitle variable starting with '\0'
bookData* TAIL = --books;
// Need all 3 pointers for a naive, in place insertion/linear sort routine
// Outputs book data following the sort
// Before returning, calls the destructors for the books in bookArray
// Also calls the destructor for books, HEAD, and TAIL as well
// ...which were already called as part of the bookArray's destructor calls
// Which is where I have my problem now
}
Bonus: globals.h
As you may have noticed, I've already attempted to check whether the bookData object has already been deleted by using if (bookTitle) in the destructor function, but it still evaluates as true when I'm running it through VS's Step Into functionality. Short of nuking the destructor all together, what can I do to get around this problem and make the destructor prematurely exit if the object in question has already been deallocated?
I've already attempted to check whether the bookData object has already been deleted by using if (bookTitle) in the destructor function
Since delete[] doesn't set the pointer to NULL, the check is effectively a no-op.
Even if you set the pointer to NULL manually, you'd be tackling the symptoms of the problem rather than the root cause.
The root cause is that you're not implementing the copy assignment operator, thereby violating the rule of three. What happens is that you're using the implicitly-generated copy assignment operator:
swap = *books;
*books = *(books - 1);
*(books - 1) = swap;
and that operator doesn't do the right thing: it copies the pointers instead of copying the data. The double deletes are a direct consequence of that.
Additionally, the implementation of the copy constructor could be buggy, but it's hard to be sure without seeing its source code.
P.S. You'd do yourself a massive favour by using std::string instead of C strings. Also, std::vector is to be preferred to C arrays.
I am cleaning up a toy program I wrote for a class using XCode 5. Part of the assignment was to gain familiarity with dynamic memory allocation (meaning it must use new and delete despite the fact that it would really work fine without it). I am receiving:
HeapOfStudents(67683,0x7fff769a6310) malloc: *** error for object 0x100300060: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug
The error happens:
Student::~Student() {
delete firstName; // <- on this line
delete lastName;
delete address;
delete birthDate;
delete gradDate;
delete gpa;
delete crHours;
}
which is being called...
int main() {
string line = "";
vector<Student> students;
//
//Create new students from file
//
ifstream data_file ("heapData");
if (data_file.is_open()) {
while(getline(data_file, line))
students.push_back(*new Student(line)); <- inside this call here
data_file.close();
}
else return 0;
The full Student class is below.
Importantly, I noticed that while each line gets correctly pushed to the vector, when the next Student gets pushed, the previous Student inside the vector gets corrupted. I see the malloc error always on the third iteration of the loop (the file has 50 lines).
The program runs without exception if I change vector<Student> to vector<Student *> and student.push_back(*new Student(line)) to student.push_back(new Student(line)).
My question then is: Can someone please explain what, on a lower level, is happening inside this loop that creates this error?
My guess is that *new Student(line) uses the same pointer each time, so through each iteration, data gets corrupted because it is being freed (although this explanation raises questions), while new Student returns a new pointer each time, preserving the data that was passed to students. I'm thinking that maybe I need to write a copy constructor... This is just my guess.
Here is the Student class. Address and Date are also custom classes that are fairly similar. I didn't include them because they don't seem to be part of the issue.
//Constructors
Student::Student() {
firstName = new string("Testy");
lastName = new string("Testerson");
address = new Address("000 Street St", "", "Citytown", "State", "00000");
birthDate = new Date("01/02/9876");
gradDate = new Date("01/02/6789");
gpa = new string("10.0");
crHours = new string("300");
}
Student::Student(string FN, string LN, string L1, string L2, string C, string S, string Z, string BD, string GD, string GPA, string Hr) {
firstName = new string(FN);
lastName = new string(LN);
address = new Address(L1, L2, C, S, Z);
birthDate = new Date(BD);
gradDate = new Date(GD);
gpa = new string(GPA);
crHours = new string(Hr);
}
Student::Student(string line) {
set(line);
}
//Destructors
Student::~Student() {
delete firstName;
delete lastName;
delete address;
delete birthDate;
delete gradDate;
delete gpa;
delete crHours;
}
//Member Functions
void Student::set(string line) {
firstName = new string(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
lastName = new string(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
address = new Address();
address->setLine1(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
address->setLine2(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
address->setCity(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
address->setState(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
address->setZip(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
birthDate = new Date(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
gradDate = new Date(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
gpa = new string(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
crHours = new string(line.substr(0, line.find_first_of(",")));
line = line.substr(line.find_first_of(",") + 1, string::npos);
}
void Student::printReport() {
cout << *lastName << ", " << *firstName << ", " << address->getAddress()
<< ", " << birthDate->getDate() << ", " << gradDate->getDate()
<< ", " << *gpa << ", " << *crHours << endl;
}
void Student::printName() {
cout << *lastName << ", " << *firstName << endl;
}
string Student::getName() {
return string(*lastName + ", " + *firstName);
EDIT Here's the copy constructors and assignment operators I wrote for the student class should anyone be interested in seeing/critiquing them:
Student::Student(const Student & student){
firstName = new string(*student.firstName);
lastName = new string(*student.lastName);
address = new Address(*student.address);
birthDate = new Date(*student.birthDate);
gradDate = new Date(*student.gradDate);
gpa = new string(*student.gpa);
crHours = new string(*student.crHours);
}
Student& Student::operator=(const Student & student) {
return *new Student(student);
}
It looks like you're not following the Rule of Three, so Bad Things will happen if you copy a Student object. Specifically, both will contain pointers to the same objects, which both will try to delete - so one will try to delete something that the other has already deleted.
The easiest way to give the class valid copy semantics is to disallow copying, by deleting the copy constructor and copy-assignment operator:
class Student {
Student(Student const &) = delete;
void operator=(Student const &) = delete;
// rest of class...
};
Otherwise, if you want the class to be copyable, implement these to do something sensible.
Also, this causes a memory leak:
students.push_back(*new Student(line));
by dynamically creating a Student, copying it into the vector, and discarding the only pointer to the dynamic object. If you're storing objects, then push a copy of a temporary:
students.push_back(Student(line));
Alternatively, you could change the container type to vector<Student*>, and remember to delete each object when you remove its pointer. (That's a reasonable thing to do as an exercise in pointer-wrangling, as you say this is, but don't do it in any program you want to maintain.)
Once you've learnt the grisly details of manual memory management, make life easier for yourself by avoiding dynamic allocation unless absolutely necessary and, when you really do need it, always managing it with smart pointers, containers, and other RAII types, not by juggling raw pointers.
The issue is likely happening because the default copy and assignment operators do not do the right thing -- they copy the pointers. So if you create a temporary Student somewhere you are going to delete the pointers when that temporary is destructed, while the original still points to the same (now deleted) objects. When it is destructed, it is going to try to delete pointers that were already deleted, hence the error.
You need to follow the rule of three: if you need a custom destructor, assignment operator, or copy constructor, you need all of them.
Add the following public members to your class:
Student(Student const &);
Student & operator=(Student const &);
And define them like so:
Student::Student(Student const & other)
{
firstName = new string(other.firstName);
// And so on, for each pointer member.
}
Student & Student::operator=(Student const & other)
{
*firstName = other.firstName;
// And so on, for each pointer member.
return *this;
}
Note that all of this can be avoided by using string firstName; instead of string * firstName; -- in that case, the default destructor, copy constructor, and assignment operator would do the right thing and you wouldn't need to define any of them.
Further, be aware that your Address and Date classes could have the same problem! You have to do these kinds of gymnastics any time you use raw pointers as class members.
One problem you have is that you are keeping a vector of Student objects, instead of a vector of pointers to dynamically allocated objects (of type Student* therefore).
Simply replace your students variable to std::vector<Student*> students;. From then, you can simply push_back the pointer created by new.
The thing is that vector already deals with memory allocation, so the line making the push (that you highlighted in the code) was making a copy of the Student to a position in the vector. The pointer to the dinamically allocated object would become unreachable after that.
As Barmar pointed out, having a vector of pointer also has the advantage of being able to push pointers of subclasses of Student. A simple example on that:
class PhDStudent : public Student { ... }
students.push_back(new PhDStudent(...));
Furthermore, there are many other tweaks you should consider in your class:
Your constructor is taking string parameters by-value, which means they are deeply copied from their origin. Using const string& is preferable here, to avoid unnecessary copies.
As already pointed out by some other answers (by Mike Seymour, cdhowie, Anton Savin and whoever is going to point this out), you should follow the Rule of Three. If you want your class to be copiable, you should also implement the copy constructor and the copy assignment operator. If you're using C++11, you can take advantage of a move constructor and a move assignment as well, so as to reduce the number of allocations when moving those objects.
It may happen that you don't want Students to be copied, but you still want to put them into a vector. In C++11 it can be solved by just adding a move constructor:
class Student {
public:
Student() { ...}
~Student() {...}
Student(const Student&) = delete; // optional: will be deleted implicitly
Student(Student&& other) {
firstName = other.firstName;
other.firstName = nullptr;
// ...
}
};
This way copy constructor and assignment operator will be implicitly deleted, so you'll not be able to make copies. But vector and other containers will use move constructor just fine. This of course puts certain limitations on Student usage.
vector<Student> students;
students.push_back(Student()); // OK, student moves
Student s;
students.push_back(s); // Error: copy constructor for Student is deleted
This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 8 years ago.
I have problem with the following class. I think the problem is with string array, cause I made two other classes and the problem was the same. When I run the program it throws "double free or corruption", but I do not think any double corruption is possible. The problem is same with input string as reference or as common argument in Add method.
class WareH
{
public:
WareH(void)
{
first = true;
rows = 1;
inLine = 0;
cnt = 0;
max = 2;
cL = 0;
strs = new string[max];
}
~WareH(void)
{
delete [] strs;
}
bool Add(string& str, int ending)
{
if (first)
inLine++;
else
cL++;
if (ending == 0)
{
if (first)
first = false;
if (cL != inLine)
return false;
rows++;
}
strs[cnt++] = str;
Bigger();
return true;
}
void Bigger(void)
{
if(max == cnt)
{
max *= 2;
string* tmp = new string[max];
for (int i = 0; i < cnt; i++)
tmp[i] = strs[i];
delete [] strs;
strs = tmp;
}
}
friend ofstream& operator<<(ofstream& of,WareH war)
{
for (int a = 0; a < war.cnt; a++)
of << war.strs[a] << endl;
return of;
}
private:
bool first;
int rows, inLine, cnt, max, cL;
string* strs;
};
When a class manages resources, and releases them in its destructor, you must consider the Rule of Three to make sure that copying an object will not result in two objects managing the same resource.
That is what is happening here: the default copy constructor and copy-assignment operator will copy the pointer, giving you two objects which will both try to delete the same array on destruction. Solutions are:
Delete the copy constructor and copy-assignment operator to prevent copying; or
Implement them to copy the strings into a new array, not just the pointer; or
Use std::vector rather than messing around managing memory allocation yourself.
When I run the program it throws "double free or corruption", but I do not think any double corruption is possible.
Educated guess here:
The problem is not in the code you've shown, but in the client code. Here's what I think happens:
you wrote client code that instantiates (or assigns or returns by value or stores in a std container) WareH instances, and since you do not define a copy constructor and assignment operator (see "The Big Three"), they end up copying the values from your source objects. When the first of these instances (that are assigned to each other) are deleted, they delete the strs pointer.
When the second instance is deleted, they delete the same strs pointers that were deleted before (because the default copy constructors and assignment operators do not duplicate the allocated memory but just copy the pointers).
Solutions (if that is indeed, the problem):
working (and bad) solution: explicitly define copy construction and assignment operator for your class.
working (and good) solution: implement your strs as a std::vector<std::string> instead of std::string* and cnt.