views:

190

answers:

8

Okay, I'm trying to make a quick little class to work as a sort of hash table. If I can get it to work then I should be able to do this:

  StringHash* hash = new StringHash;
  hash["test"] = "This is a test";
  printf(hash["test"]);

And it should print out "This is a test".

It looks like I have 2 problems at this point. Firstly I did this:

const char* operator[](const char* key) {
  for(int i = 0; i < hashSize; ++i) {
    if(strcmp(hkeys[i], key) == 0) {return values[i];}
  }
  return NULL;
}

But when I try to look up a value the compiler complains that

error: invalid types `StringHash*[const char[5]]' for array subscript

Secondly operator[]= does not appear to be the correct syntax here. The only other thing I could find was &operator[] but I don't think that will work since I have to code the lookup procedure??? (Isn't that syntax just used to return an array item reference?)

Is what I'm trying to do here even possible? Any advice appreciated. :)


Seems to be some confusion about what I'm trying to do. I'll post my code:

http://pastebin.com/5Na1Xvaz


Finished product after all the help:

http://pastebin.com/gx4gnYy8

+6  A: 

The error is because hash is a pointer. Change to:

StringHash hash;
ybungalobill
Good catch. Thank you. It's not giving a compile error there now. Initial test works, so I can look up values in the hash by their keys now.
ddd
+3  A: 

hash isn't a StringHash object. Its a pointer to one.

You can do this:

(*hash)["test"] = "This is a test";

Or you can ask yourself why you need a pointer to it in the first place,

StringHash hash;
hash["test" = "This is a test";

... or even if you do, why you wouldn't use a smart pointer like auto_ptr.

#include <memory>
std::auto_ptr<StringHash> hash( new StringHash );
(*hash)["test"] = "This is a test";
John Dibling
+3  A: 

Write StringHash hash; instead of the new thing. C++ isn't Java. :-)

Cheers & hth.,

– Alf

Alf P. Steinbach
+3  A: 

The other answers relate to your first question. As for your second...

If you return a reference, then you're returning an lvalue. You can always assign to an lvalue.

Yes, it (pretty much) really is that simple. I recommend reading carefully for whether or not you need const in various places, though.

What I remember reading is that you should provide a const and a non-const overload for operator[], something like so:

MyType const &operator[](int index) const; // This is the array access version (no assignment allowed), which should work on const objects
MyType &operator[](int index);      // This is the array access or assignment version, which is necessarily non-const.

See this link for more information.

Platinum Azure
+2  A: 

The first error is that you declared hash to be a pointer. Pointer types can already be used with the index operator. For example, pointer[3] is equivalent to *(pointer+3). You can't change that behaviour. Make hash the object itself:

StringHash sh;

As for operator[]=, there is no such thing. Your index operator should just return a reference in order to make the assignment work. Here's a simple example of how this would look like:

class Indexable
{
   std::string arr[3];
public:
   std::string & operator[](int index) {
      return arr[index];
   }
   std::string const& operator[](int index) const {
      return arr[index];
   }
};
sellibitze
+2  A: 

Five problems:

  1. hash is a pointer to a StringHash, you have to dereference it to use operators: (*hash)["test"]
  2. If you want to assign to the element, you have to return a reference to the element type

    const char *& operator[] (const char* key);

    // ...

    (*hash)["test"] = "This is a test"; // will compile now

  3. null isn't a keyword in C++. Use 0 or NULL.

  4. operator [] has to allocate space for an element if it isn't found. Returning NULL isn't an option. Otherwise, trying to assign to the result of (*hash)["test"] will crash your program.
  5. Use std::map or std::tr1::unordered_map instead of writing your own "quick" class.

And just to be a jerk: You know that isn't a hash table, right?

Steve M
1. Fixed. ty2. I'm trying to overload the element assignment because I want to strcpy the rval.3. Noticed that just after I posted. Fixed. ty4. See 2.5. Don't wanna. :)Why is it not a hash table?
ddd
@ddd: It's not a hash table because it doesn't calculate the keys' hash values in order to index into the array. See http://en.wikipedia.org/wiki/Hash_table
Steve M
Ah. I see what you mean. A regular table then I guess.
ddd
+1  A: 

Are you able to use a boost::unordered_map<std::string, std::string? Then you don't have to worry about implementing this on your own.

Assuming it's an exercise of some sort for yourself: You may come from different background, but in C++ the normal way to declare your hash would be:

StringHash hash;

Additionally your operator[] might work for the print but it won't work for the assignment. Normally operator[] methods work by returning a non-const reference or a proxy object to which the new values can be assigned, and yours does neither. If you were able to use std::string, you could rewrite your method to return a non-const reference to the position within the hash that should be read or assigned.

Mark B
+1  A: 

I would firstly question why you are writing your own HashMap when there are some versions available albeit not a standard one.

Does your hash-map store const char* pointers or std::strings? (It might store const char * pointers if it is simply a lookup table to data stored elsewhere that is not going to change its lifetime).

What is operator[] supposed to do when the item is not found?

Now let me assume that the answers are: - Yes we are storing const char * pointers, and we store a NULL in an empty cell - When we perform hash[key]=value we want to associate key with value - If we just do hash[key] but don't write, it does not insert

This can be done with a magic object: when you assign a const char * to this object it inserts or overwrites to the hash. You can also have an implicit conversion from the object to const char * for reading.

This is quite complex though and it is preferable to stick to the regular interface of map: operator[] always inserts and you use a different method just to lookup.

CashCow
Hmm... I think I understand what you're saying. You want me to write a class with an operator=() and then have that handle adding the entry to the table, yes? I can see that working. The thing I'm worried about though, wouldn't I have to store another array internally that would hold an instance of that class for every... Ah, no wait. I could just use those objects as the internal values instead of const char* and then have them return the string on lookup. Got it. :)
ddd
Shoot. It won't let me do it this way. It keeps trying to return the magic object instead of the cstring.
ddd
You can define an implicit conversion between the "magic" type and `const char*`. Using `std::strings` is a more robust solution, though.
Steve M
Yeah. That's where I'm stuck. How do I define an implicit conversion to const char*? I'm trying to find info on that now.
ddd
I found a site that says to use 'operator const char*()'. I'm trying to fiddle with that now. (Never seen a type as an operator before.)
ddd
Okay. Now it works, which is awesome, but it also core dumps when main() returns. ARGH... Maybe a problem with the dtors... :(Anyway, thank you all! :)
ddd
Works perfectly now! This is why I re-invent the wheel, btw. I unexpectedly learned several new things just writing this small class! Thanks again to everybody. :)
ddd
Now you have done that your next exercise is to make it const-correct. For that to happen, if you have a const reference to your hash table it will let you read but not insert. Hint: operator[] should have two overloads, and the const-one gives you a genuine const char *.
CashCow