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
Related
In C++, with Visual Studio Code,
I made class Person.
I made a vector <Person*> phonebook.
I constructed an object Person newperson and pushed it in my vector phonebook
And I want to access newperson.print().
So I wrote phonebook[i]->print but it shows a runtime error.
I thought it was a copy constructor problem, but after I added it, it was the same. And I also tried to separate by constructing a new dummy object, but it doesn't work either. How can I access to object's method through object's pointer stored in vector?
Person(const Person &oldperson)
{
firstname = oldperson.firstname;
lastname = oldperson.lastname;
phonenumber = oldperson.phonenumber;
}
//This is my copy constructor,
void PrintAll()
{
for(int i = 0; i < phonebook.size(); i++)
{
cout << "Hello " << i << endl;//it runs
Person personprinter = Person(*phonebook[i]);
cout << "Hello 2" << endl;//it does not run
personprinter.Print();
}
}
//This is my method where I will call object's method.
Expected: .Print() runs.
Actual: Person personprinter = Person(*phonebook[i]); doesn't run.
I think we need definition of phonebook and also to see the way you added object in it.
The object you added in phonebook have gone out of scope thus they have been deleted (you did not do a new to build the object).
Then you are using a pointer to a deleted object.
{
Person newperson = Person(nameinarray[0], nameinarray[1], phonenumber);
phonebook.push_back(&newperson);
}// closing the curly brace here calls delete on newperson
// pointer in phonebook now points to a deleted object
VS
{
Person* newperson = new Person(nameinarray[0], nameinarray[1], phonenumber);
phonebook.push_back(newperson);
}
// pointer in phone book is still valid
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 tried to search same questions, but not one helped me. When I run program I get the "A Buffer Overrun has occurred..." error.
Constr:
Player(char* n)
{
length = strlen(n);
name = new char[length+1];
for(unsigned int i(0); i < length; i++)
name[i] = n[i];
name[length] = '\0';
}
Destr:
~Player(void)
{
delete [] name;
}
I've NULL terminated string and don't get out of bounds, what is problem?
There's no obvious error in the code you've posted, but trying to manage dynamic memory by juggling raw pointers will almost inevitably lead to errors like this.
Perhaps you haven't correctly implemented or deleted the copy constructor and copy-assignment operator, per the Rule of Three. In that case, copying a Player object will give two objects with pointers to the same array; both of them will try to delete that array, giving undefined behaviour.
The simplest solution is to manage your string with a class designed for managing strings. Change the type of name to std::string, and then the constructor can simply be something like
explicit Player(std::string const & n) : name(n) {}
and there's no need to declare a destructor (or move/copy constructor/assignment operators) at all.
So... a solution by using an std::string has been provided, but let me give another solution, keeping your member variables intact.
The problem is this. Suppose you have this code somewhere:
Player p1("Bob"); // Okay
Player p2("Annie"); // Okay
p2 = p1; // Oops! (1)
Player p3(p1); // Oops! (2)
At (1), the method Player& Player::operator=(const Player&) is called. Since you didn't provide one, the compiler generates one for you. When it does, it simply assumes that it may copy over all member variables. In this case, it copies over Player::name and Player::length. So, we have p1.name == p2.name. Now when the destructor of p2 is called, the allocated memory pointed to by p2.name is deleted. Then when the destructor of p1 is called, the same memory will be deleted (since p1.name == p2.name)! That's illegal.
To fix this, you can write an assignment operator yourself.
Player& Player::operator = (const Player& other)
{
// Are we the same object?
if (this == &other) return *this;
// Delete the memory. So call the destructor.
this->~Player();
// Make room for the new name.
length = other.length;
name = new char[length + 1];
// Copy it over.
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
name[length] = '\0';
// All done!
return *this;
}
At (2), the same problem occurs. You do not have a copy constructor, so the compiler generates one for you. It will also assume that it may copy over all the member variables, so when the destructors get called, they'll try to delete the same memory again. To fix this, also write a copy constructor:
Player::Player(const Player& other)
{
if (this == &other) return;
length = other.length;
name = new char[length + 1];
for (unsigned int i = 0; i < length; ++i) name[i] = other.name[i];
}
At the end of the day you should use an std::string though.
I have a program that has a memory leak and every time I add delete to my code it crashes. I am just wondering if anyone has any idea why this is happening. The code that crashes is below.
Here is the desturctor with the delete
the first and last name are declared as follows.
char* firstName;
char* lastName;
Name::~Name(){
delete[] firstName;
delete[] lastName;
}
and here is where the memory is allocated
Name::Name(Name& name){
//copys the first and last name from one Name to the other
firstName = new char [strlen(name.firstName)+1];
strcpy(firstName,name.firstName);
lastName = new char [strlen(name.lastName)+1];
strcpy(lastName,name.lastName);
}
Most likely you are assigning an object of your Name type at some point and there is no copy assignment (first I thought there is no copy constructor but you actually show your copy constructor). The default generated copy assignment just does a bitwise copy. As a result, you see a double delete[] of your pointers at some point (there is also a memory leak but this isn't as visible). Alternatively, how do your other constructors look like? Do have a default constructor possibly not initializing the pointers or a constructor which may end up storing pointers to string literals?
The best approach is not to use manual memory allocation but rather to use std::string. If you can't use std::string, e.g., because this is an assignment, I strongly recommend that you implement your own simple string class: dealing with multiple allocated entities without wrapping each individually into a suitable resource maintaining class is extremely hard. I can't do it correctly. Admittedly, I'm only programming in C++ since about 20 years.
For example, your copy constructor is not exception safe: if your second allocation throws an exception because there isn't enough memory to allocate, you have a resource leak. There are ways to deal with function-level try/catch blocks but it is much easier to use a string class: any fully constructed subobject is automatically destroyed if a constructor throws an exception. This way, the string's constructor takes care of the memory.
Assuming that firstName and lastName are proper elements of your Name class, you should take care that strlen doesn't check against NULL and so you should deal either with NULLs
Name::Name(const Name& name){
if (name.firstName) {
int len_z = strlen(name.firstName)+1;
firstName = new char [len_z];
strncpy(firstName, name.firstName, len_z);
// ^^^ also copy the terminator char ^^^
} else {
firstName = NULL;
}
// repeat for lastName:
if (name.lastName) {
int len_z = strlen(name.lastName)+1;
lastName = new char [len_z];
strncpy(lastName, name.lastName, len_z);
} else {
lastName = NULL;
}
}
or create standard Name objects with a constructor like this:
Name::Name() {
firstName = new char[1];
firstName[0] = '\0';
// repeat for lastName:
lastName = new char[1];
lastName[0] = '\0';
}
and at least here you see, why std::string is a much better choice: it's a class that implements all the aspects of strings you need. If you make this by hand, you have to double all of it for firstName and lastName ... and imagine there will a be soon a middleName in the next version of your Name class ...
noob here. I'm doing an exercise from a book and the compiler reports no errors, but the program crashes when I try to run it.
I'm trying to run a little program exercising the different methods of the Cow class. It explicitly has: a constructor, a default constructor, a copy constructor, a destructor, an overloaded assignment operator, and a method to show its contents.
I'll put the whole project:
Class specification:
//cow.h -- For project Exercise 12.1.cbp
class Cow
{
char name[20]; // memory is allocated in the stack
char *hobby;
double weight;
public:
Cow();
Cow(const char *nm, const char *ho, double wt);
Cow(const Cow &c);
~Cow();
Cow & operator=(const Cow &c);
void ShowCow() const; // display all cow data
};
Methods implementation:
// cow.cpp -- Cow class methods implementation (compile with main.cpp)
#include <cstring>
#include <iostream>
#include "cow.h"
Cow::Cow() // default destructor
{
strcpy(name, "empty");
hobby = new char[6]; // makes it compatible with delete[]
strcpy(hobby, "empty");
weight = 0.0;
}
Cow::Cow(const char *nm, const char *ho, double wt)
{
strcpy(name, nm); // name = nm; is wrong, it copies the address of the argument pointer (swallow copying)
/*if (name[20] != '\0') // if it's not a string, make it a string (in case nm is larger than 20)
name[20] = '\0';*/
hobby = new char[strlen(ho) + 1]; // allocates the needed memory to hold the argument string
strcpy(hobby, ho); // copies the pointed-to data from the argument pointer to the class pointer
weight = wt;
}
Cow::Cow(const Cow &c) // copy constructor
{
strcpy(name, c.name); // copies the value to the desired address
char *temp = hobby; // stores the address of the memory previously allocated with new
hobby = new char[strlen(c.hobby) + 1];
strcpy(hobby, c.hobby); // copies the value to the new address
delete[] temp; // deletes the previously new allocated memory
weight = c.weight;
}
Cow::~Cow()
{
delete[] hobby;
}
Cow & Cow::operator=(const Cow &c) // overloaded assignment operator
{
strcpy(name, c.name);
char *temp = hobby;
hobby = new char[strlen(c.hobby) + 1];
strcpy(hobby, c.hobby);
delete[] temp;
weight = c.weight;
return *this;
}
void Cow::ShowCow() const
{
std::cout << "Name: " << name << '\n';
std::cout << "Hobby: " << hobby << '\n';
std::cout << "Weight: " << weight << "\n\n";
}
Client:
// main.cpp -- Exercising the Cow class (compile with cow.cpp)
#include "cow.h"
#include <iostream>
int main()
{
using std::cout;
using std::cin;
Cow subject1; // default constructor
Cow subject2("Maria", "Reading", 120); // non-default constructor
Cow subject3("Lula", "Cinema", 135);
subject1 = subject3; // overloaded assignment operator
Cow subject4 = subject2; // copy constructor
subject1.ShowCow();
subject2.ShowCow();
subject3.ShowCow();
subject4.ShowCow();
cin.get();
return 0;
}
I was hiding some parts of the code to locate the possible problem and it seems the progam doesn't like this two lines:
subject1 = subject3;
Cow subject4 = subject2
And in particular, in the overloaded assignment operator and the copy constructor, if I hide the delete[] temp line, the program doesn't crash.
I'm total noob and probably is something stupid but I can't see what I'm doing wrong in these definitions.
Any help?
Cow::Cow(const Cow &c) // copy constructor
{
strcpy(name, c.name); // copies the value to the desired address
char *temp = hobby; // stores the address of the memory previously allocated with new
hobby = new char[strlen(c.hobby) + 1];
strcpy(hobby, c.hobby); // copies the value to the new address
delete[] temp; // deletes the previously new allocated memory
weight = c.weight;
}
It's the copy c-tor. There is no previously allocated members. this->hobby points to random garbage. So when you delete[] temp (i.e. this->hobby before the new assignment), you get UB.
Cow & Cow::operator=(const Cow &c) // overloaded assignment operator
{
strcpy(name, c.name);
char *temp = hobby;
hobby = new char[strlen(c.hobby) + 1];
strcpy(hobby, c.hobby);
delete[] temp;
hobby = name;
weight = c.weight;
return *this;
}
hobby = name is incorrect, as it causes a memory leak + destructor will try to delete an object that was not allocated with operator new[].
#ForEveR Already noted that your copy constructor is deleting memory that was never allocated.
But I'm going to argue that the entire exercise is flawed and is teaching C with classes, NOT C++. If you used string all your problems would go away because you wouldn't have to manage resources directly. This eliminates the need for you to write a destructor or copy assignment/constructor entirely. For example:
class Cow
{
std::string name;
std::string hobby;
double weight;
public:
Cow();
Cow(const std::string& nm, const std::string& ho, double wt);
void ShowCow() const; // display all cow data
};