So say I have
Class Person{
private:
Person* _friends[10];
public:
//All methods listed below. Except for the main one, that is.
}
Person::Person()
{
for(int i=0; i < 10; i++)
_friends[10] = 0;
}
Person* Person::getFriends()
{
return _friends;
}
void Person::addFriend(Person* buddy) //Assume it's fully functional.
{
//code for adding a friend.
}
void Person::operator=(Person& p)
{
for(int i = 0; i < 10; i++)
_friends[i] = p.getFriends()[i];
}
int main()
{
int user1size = 10;
int user2size = 30;
Person* user1 = new Person[10];
Person* user2 = new Person[10];
//assume these 4 next lines will be repeated with different values a few more times, in more intricate ways, by the magic of for/while loops.
user1[0] = new Person();
user2[9] = new Person();
user1[0].addFriend(user[9]);
user1[9].addFriend(user[0]);
for(int i = 0; i < user1size; i++)
{
user2[i] = user1[i]; //Here is where the paradox starts.
}
delete [] user1;
return 0;
}
So well, my question is where does it stop? I mean, my intent is to only copy the reference to the Person objects inside the _friends array, but I understand that might be little to no possible, so the solution is to keep "copying" these Person objects too, who in turn also have a list of friends, and these friends also have friends and so on. I know it has to stop somewhere, but I don't really want it to go all the way through and not to even mention I don't want some Person objects to be over-copied or catch in an infinite loop.
so how to solve this paradox?
P.S. : Please understand if my code has a lot of mistakes or is crappy. I made it in the go and all I intend with it is to make you all catch the general idea.
P.S.2: I cannot use any other library other than <ostream>, yup sucks for me.
P.S.3: Any website/article you could provide me to read about this would make me so happy! Thank you beforehand.
Well, first of all, what you probably want to do is to just copy the pointers as they are.
Why? Because if person "A" has a friend "F", and person "B" has the friend "F" as well, they will probably refer to the same person "F" object. If you make a deep-copy as you suggested, and you copy A to A2 and B to B2, you will have a total of three distinct person "F" objects -- the original, referenced from A and B, a copy referenced from A2, and another copy referenced from B2.
Of course, this might be what you want; I can't know that. But in many cases it's not.
The whole pointer setup will work just fine, although you have to be careful with memory management: you can easily end up with pointers to persons that no longer exist
If you do want to do a deep copy (a "deep copy" is a copy where referenced objects are copied too) you'll have to find cycles yourself, for example by tracking which objects you have copied already. I won't be trying to sketch something like this now, as it can get somewhat complicated and I don't think you actually want to deep copy your "person" objects.
There is no paradox because you are not doing a deep copy. Check out this declaration:
Person* _friends[10];
This says _friends is an array of pointers to Person objects. Correctly copying in operator=() this array will not call operator=() on each Person in the array.
However, the following function will not compile:
Person* Person::getFriends()
{
return _friends;
}
The return type and the type of _friends does not match. This is why it is important to post code that compiles and exactly reproduces the problem you have encountered.
IIUC, you want to know how to handle copies of graphs of objects, and you want to maintain their connections in the graph. Let's say Joe has a friend Bill and Bill has a friend Joe, and you copy Joe (lets name the copy Joe'), you want Joe' also have a friend who has (reflexively) Joe' as a friend. This problem typically comes up when programming compilers.
The easiest way to actually do this is never do any copies. Let the objects reside in their memory locations, use references/pointers to them all the time and modify them destructively.
However, it is sometimes needed to copy the objects, possibly modifying the graph (in the compiler scenario, that might mean eg. inlining a function). In that case, you employ a simple graph traversing algorithm, the DFS, roughly like this:
// o will be the object to copy
// m will be a map (old object -> new object)
copy(object* o, map& m) {
if(m.contains(o))
return m[o];
object* n = allocate new object;
m[o] = n; // set the map beforehand, in case we have some back edges from this subtree
n.subobject = copy(o.subobject, m); // for each subobject, or relation. In your case, friend
return n;
}
If you want just to move some object to a new location, you can just traverse the whole graph and overwrite the pointers. This is much easier if you have back edges in your graph, too (ie. not only "friends", but also "people whose friend I am"). Still, the best option is to avoid it; that means not creating arrays of Persons that might grow and make arrays of Person*s, so that the real Person still stay in the same memory location.
Related
I am trying to change a class member's value that created in heap with address and getting the error down below.
class class2 {
private:
string String = "x";
public:
string function() {
return String;
}
};
class class1 {
public:
string String;
class2* i;
void address(class2* x) {
x = new class2();
i = x;
}
void function(string x) {
String = x;
}
};
int main() {
int len;
cin>>len;
class1 **Class1 = new class1*[len];
for(int i = 0; i < len; i++) {
Class1[i] = new class1[i];
}
Class1[0]->address(Class1[0]->i);
Class1[0]->function(Class1[0]->i->function());
cout<<Class1[0]->String;
}
Exception thrown at 0x0F4D514F (vcruntime140d.dll) in Project70.exe:
0xC0000005: Access violation writing location 0xDDDDDDDD.
This call of the new operator
Class1[i] = new class1[i];
is invalid. You mean
Class1[i] = new class1;
Pay attention to that this member function
void address(class2* x) {
x = new class2();
i = x;
}
does not make great sense because the passed argument to the function is not used.
Okay, lets take this step by step, to illustrate what's going wrong. It's pretty clear you're learning C++, so I'll try to write to that audience.
int len;
cin>>len;
1) read in a length from standard in. No serious problems yet. You could check to be sure it's not negative or zero, but we'll let that slide for now. We'll pretend that "len = 3", and move on from here.
class1 **Class1 = new class1*[len];
2) you create a pointer to pointer to class1 called Class1. I can't say that I like your naming scheme, but lets work passed that too, and set Class1 to an array of pointers to class1. So far so good. If len were zero or negative, you can imagine the problems you might have. Note that none of the memory for those pointers has been initialized, just set aside to hold things you put in it later.
for(int i = 0; i < len; i++) {
Class1[i] = new class1[i];
}
3) This will create some new instances of class1, and point Class1[I] at them. With len = 3, this will create a weird grid of allocations. The first array will be of length ZERO, the second of length 1, and the third of length 2. ZERO length arrays are bad.
So with that zero length array ready to blow up like a land mine when we step on it, lets go for a walk!
Class1[0]->address(Class1[0]->i);
First of all, this is pretty misleading. It would be better written as Class1[0][0].address(Class1[0][0].i); Second, passing 'i' into a function whose sole purpose is to write to a member variable that happens to be that parameter you just passed in is pointless. Just write to the member variable.
And third: Boom. We just stepped on the zero-length-array-land-mine. But all is not lost.
You can salvage this with a relatively small number of changes.
Class1[i] = new Class1[len]; will give you a grid of allocations (3, 3, 3) rather than a triangle (0, 1, 2).
for every call to new you need a call to delete, preferably in that class's destructor. Destructors are awesome. You call new in address (remove it's parameter, you don't need it), so somewhere need to call delete. Also, you should set i to nullptr in the constructor so you don't accidentally try to delete something invalid.
for every call to new[] you need a call to delete[]. You looped through an array calling new[], so you need to loop through that array calling delete[] to clean it up.
Variable names. I'm guessing English isn't your first language, and you're learning C++ too, so I understand. However, your function and variable names are meaningless at best (class1, class2, i, x), and misleading at worst (address doesn't take anything's address, it creates something). Even with not much to go on, you could at least use names like "InnerClass", "OuterClass", "message", "createInner", "innerPtr"... and they wouldn't be nearly as difficult to figure out.
<RANT MODE>
What are we, mathematicians?! We have all the space we need to describe things, so be descriptive. Mathematicians used to be confined to a blackboard, and had to write everything by hand, so some pressure to use (overly) concise (impossible for outsiders to comprehend) notations was understandable... but now? They don't eve have that excuse. And I'm looking at you too Physicists! Yeah you, in the lab coat!
</RANT MODE>
And learn to touch type. Programmers end up typing a LOT, and looking from screen to keyboard and back kills your typing speed.
So, I have an array of a class called "Customer"
Customer** customersarray[] = new Customer*[customer];
I'm receiving int customer with cin.
anyways, in customer.cpp, there is a method called void deactivate().
which goes like this:
void Custmoer::deactivate()
{
if (this != NULL)
remove this;
//this = NULL; I want to do this but it doesn't work.
}
and the purpose of this is to remove it from customer array when satisfies a certain condition. So for example,
for (int i = customer - 1; i >= 0; i--)
{
if (customersarray[i]->getAngerLevel() == 5) {
customersarray[i]->deactivate();
}
for (int z = i; i < customer - 1; i++) {
*(customersarray + z) = *(customersarray + z + 1);
}
customer--;
}
so my first questions are:
why does this = NULL not work?
is there a simpler way to remove something from pointer array when a condition is satisfied? (for example, remove all customers that has anger level of 5.)
Your mistake is thinking that you can remove something from a Customer* array by some magic inside the Customer class, but that's not true. Just remove a customer from the customer array where ever the customer array is. For instance using remove_if
#include <algorithm>
Customer** customersarray = new Customer*[customer];
...
customer = std::remove_if(customersarray, customersarray + customer,
[](Customer* c) { return c->anger() == 5; }) - customersarray;
This updates the customer variable to be the new size of the array, but doesn't free or reallocate any memory. Since you are using dynamic arrays and pointers you are responsible for that.
Which is why you should really not be using pointers or arrays, but using vectors instead.
std::vector<Customer> customerVector;
Life will be so much simpler.
Type of "this" is a constant pointer which means you cant change where it points
Your function can return a boolean and if its true just set your pointer to null
You'll be much better off using a std::vector, all memory memory management gets much safer. You cannot modify the this pointer, but that would be meaningless anyway:
It is a local variable, so any other pointer outside would not be changed, not even the one you called the function on (x->f(): the value of x is copied into this).
It contains the address of the current object - the current object is at a specific memory location and cannot be moved away from (not to be mixed up with 'moving' in the context of move semantics!).
You can, however, delete the current object (but I don't say you should!!!):
class Customer
{
static std::vector<Customer*> customers;
public:
void commitSuicide()
{
auto i = customers.find(this);
if(i != customers.end())
customers.erase(i);
delete this;
}
}
Might look strange, but is legal. But it is dangerous as well. You need to be absolutely sure that you do not use the this pointer or any other poiner to the current object any more afterwards (accessing non-static members, calling non-static functions, etc), it would be undefined behaviour!
x->commitSuicide();
x->someFunction(); // invalid, undefined behaviour!!! (x is not alive any more)
Similar scenario:
class Customer
{
static std::vector<std::unique_ptr<Customer>> customers;
public:
void commitSuicide()
{
auto i = customers.find(this);
if(i != customers.end())
{
customers.erase(i); // even now, this is deleted!!! (smart pointer!)
this->someFunction(); // UNDEFINED BEHAVIOUR!
}
}
}
If handling it correctly, it works, sure. Your scenario might allow a much safer pattern, though:
class Customer
{
static std::vector<std::unique_ptr<Customer>> customers;
public:
Customer()
{
customers->push_back(this);
};
~Customer()
{
auto i = customers.find(this);
if(i != customers.end())
customers.erase(i);
}
}
There are numerous variations possible (some including smart pointers); which one is most appropriate depends on the use case, though...
First of all, attending to RAII idiom, you are trying to delete an object before using its destructor ~Customer(). You should try to improve the design of your Customer class through a smart use of constructor and destructor:
Customer() {// initialize resources}
~Customer() {// 'delete' resources previously created with 'new'}
void deactivate() {// other internal operations to be done before removing a customer}
Then, your constructor Customer() would initialize your internal class members and the destructor ~Customer() would release them if necessary, avoiding memory leaks.
The other question is, why do you not use another type of Standard Container as std::list<Customer>? It supports constant time removal of elements at any position:
std::list<Customer> customers
...
customers.remove_if([](Customer foo) { return foo.getAngerLevel() == 5; });
If you only expect to erase Customer instances once during the lifetime of the program the idea of using a std::vector<Customer> is also correct.
I'm an expert level Java programmer, trying to port my knowledge over to C++. This is not homework, just a concept that I'm trying to learn the C++ equivalent of.
What I'm trying to do, is "generate" a list of objects of a custom type using a loop. This is how I would do it in Java:
public class TestClass
{
private ArrayList<ExampleClass> _exampleObjects;
private int _numObjects = 10;
public void populateList()
{
_exampleObjects = new ArrayList<ExampleClass>();
for(int i = 0; i < _numObjects; i++)
{
_exampleObjects.add(new ExampleClass());
}
}
public void doStuffWithListItems()
{
for(ExampleClass e : _exampleObjects)
{
e.performAction();
}
}
}
Super simple stuff. Create a list, iterate through an arbitrary loop and add objects to it. Then, loop through those objects and use them for whatever purpose.
TestClass.h:
class TestClass
{
public:
// Constructor, copy constructor, destructor definitions
void populateList();
void doStuffWithListItems();
private:
std::vector<ExampleClass> _exampleObjects;
const int _numObjects = 10;
};
TestClass.cpp:
void TestClass::populateList()
{
for(int i = 0; i < _numObjects; i++)
{
ExampleObject obj;
_exampleObjects.push_back(obj);
/* What actually goes here in place of obj? */
}
}
void TestClass::doStuffWithListItems()
{
for(auto it = _exampleObjects.begin(); it != _exampleObjects.end(); it++)
{
/* What do i do with my iterator to access my object? */
}
}
Its my understanding that where I initialise my objects in the first loop, they go out of scope and die by the end of each loop iteration. Is that right? If so, how do I make a persistent instance?
I experimented with the shared_ptr<> from and was apparently able to store them persistently, but couldn't for the life of me work out how to dereference from an iterator of a shared_ptr<>.
I feel like this should be a really simple concept. I just can't seem to work it out. I've read a lot on C++ scope and loops. I just can't seem to find anything on both.
ExampleObject obj;
_exampleObjects.push_back(obj);
/* What actually goes here in place of obj? */
Nothing. What you have is correct, assuming ExampleClass has a working copy constructor. If your compiler supports C++11 (and since you're using auto, it at least partially does), you can save yourself a copy.
_exampleObjects.emplace_back();
This constructs an object in place in the vector, forwarding the arguments (none in this case) to a matching constructor (the default ctor, in this case). For accessing the object from the iterator, do this:
for(auto it = _exampleObjects.begin(); it != _exampleObjects.end(); it++)
{
it->performAction();
}
Again, C++11 can make things better here.
for(auto & obj : _exampleObjects)
{
obj.performAction();
}
Its my understanding that where I initialise my objects in the first
loop, they go out of scope and die by the end of each loop iteration.
Correct.
If so, how do I make a persistent instance?
vector<>::push_back takes care of this. It copies the parameter into the vector. In other words, it's not the same object that was created in the loop, it's a copy. You just need to ensure that ExampleClass has non-broken copy semantics.
couldn't for the life of me work out how to dereference from an
iterator of a shared_ptr<>
If you had an iterator into a vector of shared pointers, (call it it), you would dereference it, and call the member function of the stored object, like this:
(*it)->performAction();
// alternatively
(**it).performAction();
The ideal answer suggests a very bad idea - use post increment ++ on iterator in loop.
You should never ever use it in loops where you only need to iterate because postincrement must return the value the iterator had before it was incrementing; so, that previous value needs to be copied somewhere before.
It is just not good from performance perspective and a bad codestyle sign.
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 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.