Overloading << operator - c++

I have to make for a project an Album class which uses a Song class. I seem to have problem with overloading the << operator for the Album class.
So this is the album class
const int Empty=-1
class Album
{
Song* songs;
char* name;
int top=Empty;
public:
Album ();
Album (const char*);
~Album();
friend std::ostream& operator<<(std::ostream&,Album&);
void addSong(Song&);
};
I'm worried that there might be a mistake with my functions so far but I can't find it.
the program starts but crashes ,any ideas why ? I don't think the problem is in songs[top]=p, because i have redefined the assignment operator for the Song class.
void Album::addSong(Song& p)
{
++top;
songs[top]=p;
}
Album::~Album()
{
delete[] name;
}
std::ostream& operator<<(std::ostream& os, Album &p)
{
os<<"Album name:"<<p.name<<std::endl;
os<<"Songs in the album:"<<std::endl;
for(int i=0;i<=p.top;i++)
std::cout<<p.songs[i];
return os;
}
Album::Album()
{
top=Empty;
name=new char [1];
name[0]='\0';
}
Album::Album(const char*p)
{
delete [] name;
int len1=strlen(p);
name=new char [len1+1];
strcpy(name,p);
}

You don't want:
delete [] name;
in your constructor, and want:
return os;
at the end of operator<<().
Also Songs isn't used.

Your overloaded operator is a little broken (as the comments pointed out) but is unlikely to crash your program. There are many problems with this code that will crash it.
To start with, songs is not initialized, which means that your songs[top]=p; are actually storing the value of p to somewhere in the memory pointed to by stack garbage. That will likely crash your code. You are also not actually allocating storage space to store the songs you added. Note that even if you initially allocated space to store one Song, you can't just increment top and store the next Song; your addSong will have to dynamically grow the allocated memory. Now, if you have a cap on the number of Songs an album can store, you can use the songs=new Song[MAX_SONGS] approach suggested (and make addSong fail if the album hit its max capacity), but it's far better to use vectors and strings instead of pointers and raw news and deletes if possible, and leave the memory management headache to the standard library. (You also didn't provide copy constructors and overloaded assignment operators; you would need both when your class does this sort of memory allocation, to prevent memory leaks and double deletion.)
Note that your constructor that takes a string is also broken; you haven't allocated any memory in name at that point so there's nothing to delete.
Edit: To elaborate on my comment below, if you have to be able to handle arbitrary numbers of songs and you have to use new and delete, then your addSong will have to do something like this (pseudocode):
void Album::addSong(Song& p)
{
new_size = size + 1;
new_songs = new Songs[new_size];
for( i = 0; i < size; i++)
new_songs[i] = songs[i];
new_songs[size] = p;
delete [] songs;
songs = new_songs;
size = new_size;
}
Your destructor will need to delete [] songs;. Your constructor should set size to 0 and songs to the null pointer (it's legal to use delete on the null pointer; it has no effect).
You should also overload the assignment operator (which should first delete your own name and songs, allocate enough memory to hold the other album's name and songs, and copy them over) and the copy constructor (which should allocate enough memory to hold the other album's name and songs and then copy them over).

One source of crashes:
Album::Album(const char*p)
{
delete [] name; // <--- right here
This will take uninitialized memory and apply delete it (it is essentially UB).
Edit (as others have pointed out): songs needs to be initialized (similar to name) before you place stuff into it.
More, to avoid a memory leak, also delete[] songs in Album::~Album.

You haven't allocated any space for songs.

Related

C++ program attempting to double dealloc custom object

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.

Possible logical flaw in a C++ test example

At my university, there is a practical programming test in C++ - and I'm stuck with an example where I am unsure about wheter or not the task in question is even valid and possible to complete correctly.
The (simple) tasks:
Complete the destructor of Person, so that the allocated name is freed again
In the main function, replace //??? with the statement required to free the previously allocated memory
At first, the tasks seemed trivial for me: For the destructor, simply write delete[] name and in the main function, use delete[] friends. Presumably, that is also what the author of this example meant us to do.
However:
There seems to be a flaw in this code example, which causes memory leaks as well as destructors to be called more than once.
The person class has no assignment operator =, meaning that as the existing Person objects such as maria are assigned to slots in the friends array in the main function, the internal allocated names are not copied. So two objects now share the same internal char* pointer! Moreover, the pointer to the name of the Person previously residing in the said array slot is permanentely lost, leading to an unavoidable memory leak.
As delete[] friends; is called - the objects in the array are destroyed - leading to their destructors being called and freeing their name members. When the program ends, though, the local Person objects in the scope of main are destructed - which of course have their name members still pointing to memory which was already freed before.
The actual question:
Is this test example flawed, or am I missing something?
Can the issues listed above be fixed if sticking fully to carrying out the given tasks (altering just the implementation of the destructor, and inserting new code at the commented part in the main function)?
..
#include <iostream>
using namespace std;
int strlen(const char *str) {
if (str==0) return 0;
int i=0;
for (; str[i]; ++i);
return i;
}
void strcpy(const char *src, char *dest) {
if (src==0 || dest==0) return;
int i=0;
for (; src[i]; ++i) dest[i]=src[i];
dest[i]=’\0’;
}
class Person {
char *name;
public:
Person(const char *str = "Susi") {
name = new char[strlen(str)+1];
strcpy(str,name);
}
Person(const Person &p) {
name = new char[strlen(p.name)+1];
strcpy(p.name,name);
}
~Person() {
//...
}
void change() {
name[4]='e';
}
ostream &print(ostream &o) const {
o<<name;
return o;
}
};
int main() {
Person maria("Maria"), peter("Peter"), franz("Franz"), luisa("Luisa");
Person mary(maria);
Person luise;
Person p(luise);
Person *friends= new Person[7];
friends[0]=maria;
friends[1]=peter;
friends[2]=franz;
friends[3]=luisa;
friends[4]=mary;
friends[5]=luise;
friends[6]=p;
friends[5]=luisa;
friends[3].change();
friends[4].change();
for (int i=0; i<7; ++i) {
friends[i].print(cout);
cout<<endl;
}
//???
return 0;
}
You are absolutely right. You can fix it by only making changes at the indicated positions, however they are going to be rather extreme:
Replace the //... inside the destructor with:
delete[] name;
}
Person& operator=(const Person& other)
{
if (this != &other) {
delete[] name; // not completely exception-safe!
name = new char[strlen(other.name)+1];
strcpy(other.name,name);
}
return *this;
Another serious problem is redefining a standard function (strcpy) with a new definition that reorders the arguments.
(See also: SQL injection attacks, which also cause existing pairs of syntax elements, frequently quotes and parentheses, to be re-paired with inserted syntax elements)
Yes, the test example is flawed, possibly it was done consciously. Class Person definitely need assignment operator, remember the Rule Of Three.
No, it's not possible. Default compiler-generated assignment operator will leak memory allocated by objects in friends array and double-delete memory allocated by auto Person objects.
For every new there should be a delete[].

Vector of Object Pointers, general help and confusion

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.

How to free memory of dynamic struct array

As someone who never dealt with freeing memory and so on, I got the task to create a dynamic array of struct and create functions to add or delete array elements. When deleting I have to free the memory which is no longer necessary.
when deleting the 2nd element of an array of the size of 3, I move the 3rd element to the 2nd position and then delete the last one. When deleting the last one, I always get an error... Is there anyone who can find an solution for me?
struct myFriend {
myFriend() {
number=0;
hobbys = new char*[10];
}
int number;
char* name;
char** hobbys;
};
int main() {
myFriend* friendList = new myFriend[10];
myFriend* tempFriend = new myFriend;
tempFriend->number=1;
tempFriend->name = "ABC";
myFriend* tempFriend2 = new myFriend;
tempFriend2->number=2;
tempFriend->name = "XYZ";
myFriend* tempFriend3 = new myFriend;
tempFriend3->number=3;
tempFriend3->name = "123";
friendList[0] = *tempFriend;
friendList[1] = *tempFriend2;
friendList[2] = *tempFriend3;
friendList[1] = friendList[2]; //move 3rd element on 2nd position
delete &(friendList[2]); //and delete 3rd element to free memory
}
Why did you create temporary variables? They're not even needed.
If you use std::vector and std::string, the problem you're facing will disappear automatically:
std::vector<myFriend> friendList(10);
friendList[0]->number=1;
friendList[0]->name = "ABC";
friendList[1]->number=2;
friendList[1]->name = "XYZ";
friendList[2]->number=3;
friendList[2]->name = "123";
To make it work, you should redefine your struct as:
struct myFriend {
int number;
std::string name;
std::vector<std::string> hobbys;
};
If you're asked to work with raw pointers, then you should be doing something like this:
struct Friend
{
int number;
char* name;
};
Friend * friends = new Friend[3];
friends[0]->number=1;
friends[0]->name = new char[4];
strcpy(friends[0]->name, "ABC");
//similarly for other : friends[1] and friends[2]
//this is how you should be deleting the allocated memory.
delete [] friends[0]->name;
delete [] friends[1]->name;
delete [] friends[2]->name;
delete [] friends; //and finally this!
And if you do any of the following, it would be wrong, and would invoke undefined behavior:
delete friends[2]; //wrong
delete &(friends[2]); //wrong
It is impossible to delete a subset from array allocated by new []
myFriend* friendList = new myFriend[10];
You have a single whole array
+------------------------------------------------------------------+
| friendList[0] | friendList[1] | ..... | friendList[9] |
+------------------------------------------------------------------+
You can not delete &(friendList[2]).
You get from C++ whole array of 10 elements.
This array starts from friendList (or &(friendList[0])).
operator delete with pointer to the address returned by new (i.e. friendList) is valid
only.
Two things I noticed. (1) You are apparently supposed to "create functions to add or delete elements" but you haven't done that, you have only created one function. (2) You are making your work harder than it needs to be by using a struct that also needs to manage memory. I suggest you use a simpler struct.
Your assignment is, in effect, to make a simple 'vector' class, so I suggest that you do that. Start with a struct that is empty. If the teacher requires you to use the myFriend struct as written, you can add that in after you finish making your vector like functions. I'm going to assume that you aren't allowed to make a class yet because most instructors make the mistake of leaving that until last.
struct MyStruct {
int value; // start with just one value here. Dealing with pointers is more advanced.
};
MyStruct* array;
int size;
int capacity;
void addMyStruct(MyStruct& value); // adds a MyStruct object to the end.
void removeMyStructAtPosition(int position); // removes the MyStruct object that is at 'position'
// I leave the functions for you to implement, it's your homework after all, but I give some clues below.
void addMyStruct(MyStruct& value) {
// First check that there is enough capacity in your array to hold the new value.
// If not, then make a bigger array, and copy all the contents of the old array to the new one.
// (The first time through, you will also have to create the array.)
// Next assign the new value to array[size]; and increment size
}
void removeMyStructAtPosition(int position) {
// If the position is at end (size - 1,) then simply decrement size.
// Otherwise you have to push all the structs one to the left (array[i] = array[i + 1])
// from position to the end of the array.
}
int main() {
// test your new class here.
// don't forget to delete or delete [] any memory that you newed.
}
The array size is fixed at 10, so you don't need to delete any elements from it. But you do need to delete the name and hobbys elements of friendList[1] (and before you overwrite it). There are two problems here:
You are setting friendList[0]->name = "ABC"; Here, "ABC" is a constant zero-terminated string somewhere in memory. You are not allowed to delete it. So you have to make a copy.
You want to delete hobby[i] whenever it was assigned. But in your code, you can't tell whether it was assigned. So you have to set every element to 0 in the constructor, so that you will later know which elements to delete.
The proper place to delete these elements is in myFriends's destructor.
It seems the point of the question is to manage a dynamic array. The main problem is that he is using an array of friendList. Use an array of pointers to friendList:
struct myFriend {
myFriend() {
number=0;
hobbys = new char*[10];
}
int number;
char* name;
char** hobbys;
};
int main() {
myFriend** friendList = new myFriend*[10];
myFriend* tempFriend = new myFriend;
tempFriend->number=1;
tempFriend->name = "ABC";
myFriend* tempFriend2 = new myFriend;
tempFriend2->number=2;
tempFriend->name = "XYZ";
myFriend* tempFriend3 = new myFriend;
tempFriend3->number=3;
tempFriend3->name = "123";
friendList[0] = tempFriend;
friendList[1] = tempFriend2;
friendList[2] = tempFriend3;
friendList[1] = friendList[2]; //move 3rd element on 2nd position
delete friendList[2]; //and delete 3rd element to free memory
}
But everybody else is right -- there are major issues around memory allocation for both 'hobbys' and for 'name' that you need to sort out separately.
To do your homework I'd suggest to learn much more about pointers, new/delete operators, new[]/delete[] operators (not to be confused with new/delete operators) and objects creation/copying/constructors/destructors. It is basic C++ features and your task is all about this.
To point some directions:
1) When you dynamically allocate the object like this
MyType* p = new MyType;
or
MyType* p = new MyType(constructor_parameters);
you get the pointer p to the created object (new allocates memory for a single object of type MyType and calls the constructor of that object).
After your work with that object is finished you have to call
delete p;
delete calls the destructor of the object and then frees memory. If you don't call delete your memory is leaked. If you call it more than once the behavior is undefined (likely heap corruption that may lead to program crash - sometimes at very strange moment).
2) When you dynamically allocate array like this
MyType* p = new MyType[n];
you get the pointer p to the array of n created object located sequentially in memory (new[] allocates single block of memory for n objects of type MyType and calls default constructors for every object).
You cannot change the number of elements in this dynamic array. You can only delete it.
After your work with that array is finished you have to call
delete[] p; // not "delete p;"
delete[] calls the destructor of every object in the array and then frees memory. If you don't call delete[] your memory is leaked. If you call it more than once the behavior is undefined (likely program crash). If you call delete instead of delete[] the behavior is undefined (likely destructor called only for the first object and then attempt to free memory block - but could be anything).
3) When you assign the struct/class then operator= is called. If you have no operator= explicitly defined for your struct/class then implicit operator= is generated (it performs assignment of every non-static member of your struct/class).

Safe Delete in C++

I have developed an array based implementation of a hashTable with several stock names, symbols, prices, and the like. I need to remove a stock from my array. I am told that using the delete operator is bad object oriented design. What is the good object oriented design for deletion?
bool hash::remove(char const * const symbol, stock &s,
int& symbolHash, int& hashIndex, int& usedIndex)
{
symbolHash = this->hashStr( symbol ); // hash to try to reduce our search.
hashIndex = symbolHash % maxSize;
usedIndex = hashIndex;
if ( hashTable[hashIndex].symbol != NULL &&
strcmp( hashTable[hashIndex].symbol , symbol ) == 0 )
{
delete hashTable[hashIndex].symbol;
hashTable[hashIndex].symbol = NULL;
return true;
}
for ( int myInt = 0; myInt < maxSize; myInt++ )
{
++usedIndex %= maxSize;
if ( hashTable[usedIndex].symbol != NULL &&
strcmp( hashTable[usedIndex].symbol , symbol ) == 0 )
{
delete hashTable[usedIndex].symbol;
hashTable[usedIndex].symbol = NULL;
return true;
}
}
return false;
}
Noticing that i have a stock &s as a parameter, i can use it like this:
s = &hashTable[usedIndex];
delete s.symbol;
s.symbol = NULL;
hashTable[usedIndex] = &s;
This does work however, it results in a memory leaks. Even then, i am not sure if it is good object orinted design.
here is my header, where stock and all that stuff is initialized and defined.
//hash.h
private:
friend class stock;
int isAdded; // Will contain the added hash index.
// Test for empty tables.
// Can possibly make searches efficient.
stock *hashTable; // the hashtable will hold all the stocks in an array
};
// hashtable ctor
hash::hash(int capacity) : isAdded(0),
hashTable(new stock[capacity]) // allocate array with a fixed size
{
if ( capacity < 1 ) exit(-1);
maxSize = capacity;
// We can initialize our attributes for the stock
// to NULL, and test for that when searching.
for ( int index = 0; index < maxSize; index++ )
{
hashTable[index].name = NULL;
hashTable[index].sharePrice = NULL;
hashTable[index].symbol = NULL;
}
}
// stock.h
...
friend class hashmap;
private:
const static int maxSize; // holds the capacity of the hash table minus one
date priceDate; // Object for the date class. Holds its attributes.
char *symbol;
char *name;
int sharePrice;
};
My question is still just, how do i preform a safe remove?
s = &hashTable[usedIndex];
delete s.symbol;
s.symbol = NULL;
hashTable[usedIndex] = &s;
That seems to work, but results in memory leaks! How is this done safely?
delete hashTable[usedIndex].symbol;
hashTable[usedIndex].symbol = NULL; <-- without doing this.
The status of the slot in the array (empty, etc) should not be recorded in the stock instance. That's bad object oriented design. Instead, I need to store the status of an array slot in the array slot itself.
How would i do that?
NOTE: This answer is just addressing
some of the things you are doing
incorrectly. This is not the best way
to do what you are doing. An array of
stock** would make more sense.
Doesn't your stock class have a constructor? You don't need to know anything about the stock class if it is a proper object:
hash::hash(int capacity) // change this to unsigned and then you can't have capacity < 0
: isAdded(0)
, hashTable(0) // don't call new here with bad parameters
{
if ( capacity < 1 ) exit(-1); // this should throw something, maybe bad_alloc
maxSize = capacity;
hashTable = new stock[capacity]; // this calls the stock() constructor
// constructor already called. All this code is useless
// We can initialize our attributes for the stock
// to NULL, and test for that when searching.
// for ( int index = 0; index < maxSize; index++ )
// {
// hashTable[index].name = NULL;
// hashTable[index].sharePrice = NULL;
// hashTable[index].symbol = NULL;
// }
}
class stock {
char* name; // these should be std::string as it will save you many headaches
char* sharePrice; // but I'll do it your way here so you can see how to
char* symbol; // avoid memory leaks
public:
stock() : name(0), sharePrice(0), symbol(0) {}
~stock() { delete[] name; delete[] sharePrice; delete[] symbol; }
setName(const char* n) { name = new char[strlen(n)+1]; strcpy(name, n); }
setPrice(const char* p) { sharePrice = new char[strlen(p)+1]; strcpy(sharePrice, p); }
setSymbol(const char* s) { symbol = new char[strlen(s)+1]; strcpy(symbol, n); }
const char* getName() const { return name; }
const char* getPrice() const { return sharePrice; }
const char* getSymbol() const { return symbol; }
}
To get good object oriented design, a collection should be agnostic of what is stored in it. This really has nothing to do with using the delete operator per se, but requiring an object (your stock in this case) to store data structure specific code is.
There are two plans things I can see to quickly fix this issue.
Use an array of stock * instead of just stock. Then a null value will mean the slot is open, and a non-null value will mean the slot can be used. In this plan you would call new and delete on the entire stock object as it is inserted and then as it is removed, which is more object oriented than just the symbol.
Create a HashSlot class that wraps the stock item, adding the book keeping values that are needed.
Your hash table would then be an array of HashSlot items.
I prefer the second. In either case, stock should have a destructor that clears up its own internal memory.
It looks like you're using (or trying to use) open addressing with linear-probing for collision resolution. In that case, you need to somehow mark items as deleted (as opposed to empty), so that you can still access items which fall after deleted items. Otherwise, you won't be able to lookup certain items because your probing sequence will be terminated prematurely if it finds a deleted bucket. Therefore, you won't be able to access certain items in the table anymore and that's probably why you're getting a memory leak.
Basically, you're supposed to start at the hash index, compare the item with your key, and then if it isn't equal to your key, increment to the next index and repeat until either you find the item, or until you encounter an empty bucket. If you find the item, delete the item and mark that index as deleted. But the important thing is that you have some way to distinguish between an empty hash bucket, and a deleted hash bucket, otherwise a deleted bucket will cause you to terminate your probing sequence early.
As for "good object oriented design", there is no inherent property of object-oriented programming that necessarily makes using delete a bad design. Every data structure that allocates memory has to free it somehow. What you're probably referring to is the fact that it's usually safer and less work to implement classes that don't manage their own memory, but rather delegate that responsibility to pre-made container classes, like std::vector or std::string
I have developed an array based implementation of a hashTable with several stock names, symbols, prices, and the like. I need to remove a stock from my array. I am told that using the delete operator is bad object oriented design. What is the good object oriented design for deletion?
Well, one of the key principles behind object oriented design is reusability.
Hence, the only good object oriented design is to reuse the solutions that have already been developed for you!
C++ comes with a perfecetly good map class. Most recent compilers also support TR1, which adds a hash table under the name unordered_map.
The Boost libraries also contain an implementations of unordered_map in case you're stuck on a compiler without TR1 support.
As for your question about delete:
I'm not sure who told you that delete is "bad object-oriented design", or why, but what they might have meant is that it is bad C++ design.
A common guideline is that you should never explicitly call delete. Instead, it should be called implicitly through the use of the RAII idiom.
Whenever you create a resource that must, at some later point, be deleted, you wrap it in a small stack-allocated object, whose destructor calls delete for you.
This guarantees that it gets deleted when the RAII object goes out of scope, regardless of how you leave the scope. Even if an exception is thrown, the object still gets cleaned up, its destructor called, and your resource deleted. If you need more complex ways to manage the object's lifetime, you might want to use smart pointers, or just extend your RAII wrapper with copy constructor and assignment operator to allow ownership of the resource to be copied or moved.
That is good C++ practice, but has nothing to do with object-oriented design. Not everything does. OOP isn't the holy grail of programming, and not everything has to be OOP. Good design is much more important than good OOP.