I am trying to make a basic HashMap. I am checking to see if an element exists at an index before inserting it there. When I insert my first element, it says that an element already exists at that position. I have gone through the debugger, and all of my values are as expected, except for map[hash]. I am anticipating a nullptr, but it is not coming. map[hash] has the following value:
- map[hash] 0xcdcdcdcd {key=??? value={...} next_element=??? } HashElement *
Can someone please explain to me what I am misunderstanding here? The unexpected result is on line 21 of HashMap.cpp. Here is the relevant code:
HashMap.h
#pragma once
#include <string>
#include "HashElement.h"
class HashMap
{
private:
HashElement **map;
int size;
public:
HashMap(int);
~HashMap();
int GetHash(int);
void Put(int, std::string);
};
HashMap.cpp
#include "HashMap.h"
#include <string>
HashMap::HashMap(int _size)
{
size = _size;
map = new HashElement*[size];
}
HashMap::~HashMap()
{
}
int HashMap::GetHash(int _key){
return _key % size;
}
void HashMap::Put(int _key, std::string _value){
int hash = GetHash(_key);
if (!map[hash]){ //Anticipated to be nullptr on first Put, but it skips to else
map[hash] = new HashElement(_key, _value);
}
else{
HashElement *lastElement = map[hash];
while (lastElement->next_element){
lastElement = lastElement->next_element;
}
lastElement->next_element = new HashElement(_key, _value);
}
}
HashElement.h
#pragma once
#include <string>
class HashElement
{
private:
int key;
std::string value;
public:
HashElement(int, std::string);
~HashElement();
HashElement *next_element;
int get_key();
std::string get_value();
};
HashElement.cpp
#include "HashElement.h"
HashElement::HashElement(int _key, std::string _value)
{
key = _key;
value = _value;
}
HashElement::~HashElement()
{
}
int HashElement::get_key(){
return key;
}
std::string HashElement::get_value(){
return value;
}
map[hash] is not a nullptr because you haven't initialized it to such.
map = new HashElement*[size];
Each element in the map array will have a random value after that line.
To fix this and initialize all elements to be nullptr:
map = new HashElement*[size]();
^^
map = new HashElement*[size];
Here, you are instantiating an array of size pointers, on the heap. As I understand your question, you are assuming that all of the instantiated pointers, in this new array, will be nullptr.
That is not the case. For "plain old data", or POD, its contents are not initialized by default. You'll have to explicitly initialize them:
for (size_t i=0; i<size; ++i)
map[i]=0;
... in the constructor
Related
Suppose I have a class called Entry:
template <typename K, typename V>
class Entry {
public:
Entry(K const &key, V const &val, size_t const hash_val) :
key(key), val(val), hash_val(hash_val), empty(false){
}
K getKey() const {
return key;
}
V getValue() const {
return val;
}
size_t getHash() const {
return hash_val;
}
bool isEmpty() const{
return empty;
}
private:
// key-value pair
K key;
V val;
// Store hash for reallocation
size_t hash_val;
// Store empty state
bool empty;
};
Then I create an array of objects
Entry<K, V>** entries = new Entry<K, V> *[100];
If I call entries[0]->isEmpty(), I get a segmentation fault. This makes sense to me, since I haven't actually instantiated a new object in memory. However, I want to be able to check whether a slot in the array actually points to a valid object. Currently, I've been setting each pointer to nullptr so I can check for equality later, but I was wondering if there was a better way.
You want optional. It's always either a valid object, or in an "empty" state.
#include <cstdio>
#include <optional>
#include <vector>
struct Foo {
int bar;
};
int main() {
std::vector<std::optional<Foo>> vfoo{
Foo{1}, std::nullopt, Foo{2}, Foo{3}, std::nullopt,
};
for (auto const& foo : vfoo) {
if (!foo)
std::puts("Not Initialized");
else
std::printf("Foo{%d}\n", foo->bar);
}
}
There is no way to check if a pointer has been initialized, because reading from an unitialized pointer is undefined behavior:
int* p;
if (p == something) ... // undefined behavior
You can initialize the pointer with nullptr and check for that:
int* p = nullptr;
if (p == nullptr) ...
However, then you are initializing the pointer.
For a dynamic array of Entry<K, V>* you can use a std::vector<Entry<K,V>*>. A container that can be used analogous to an array with empty slots is a std::unordered_map:
std::unordered_map<size_t,Entry<K,V>*> map;
Entry<K,V>* ptr = get_pointer_from_somewhere();
size_t index = 42;
if ( map.find(index) == map.end() ) {
// pointer was not initialized (actually pointer is not yet in the map)
map.insert( { index, ptr }); // now it is
}
Actually insert alone will tell you already if the element with key index was present in the map before.
Currently trying to learn c++, and I'm trying to make a "hunt the wumpus" application to help me learn. I am having trouble assigning a lambda function to an unordered_map in C++. My IDE is giving me the error
"Parameter type mismatch: Incompatible pointer types 'node *const' and 'node ()(node *)'"
#include <iostream>
#include <string>
#include <unordered_map>
class node{
public:
int title;
node *adj[8];
std::string desc;
}
...
bool shoot(node *player){
std::unordered_map<std::string, node*> adjLambda; //lambda to return *left, *up, etc
for (int i; i < 8; i++){
if (player->adj[i]->title != player->title){ //empty adjacencies points towards self
adjLambda.insert(std::pair <std::string, node*> (std::to_string(player->adj[i]->title), [](node *n) { return n->adj[i];}));
}
}
}
You can include <functional> and then store the value as a std::function:
void shoot(node *player){
std::unordered_map<std::string, std::function<node*(node*)>> adjLambda; //lambda to return *left, *up, etc
for(int i{}; i < 8; i++){
if(player->adj[i]->title != player->title){ //empty adjacencies points towards self
adjLambda.insert( // insert into the unordered map
std::pair< // pair to be inserted
std::string, // key is a string
std::function< // map entry is a function
node*(node*) // function takes a node pointer and returns a node pointer
>
>(
std::to_string(player->adj[i]->title), // create a string to use as the key
[i](node *n) { return n->adj[i]; } // lambda that takes a node pointer and returns another
)
);
}
}
}
I am having some trouble figuring out why i get Unhandled exception at 0x003DBD00 in Project10.exe: 0xC0000005: Access violation reading location 0xCDCDCDE5. It seems to pop up at a string assignment operator similar to this: std::string x = std::move(std::string y); Please see code below for more information.
The call starts from here: *members += NVPair<std::string, std::string>(n,v);
FYI members is members = new List <NVPair <std::string, std::string>, DATA_MEMBERS_PER_OBJECT>(); where DATA_MEMBERS_PER_OBJECT is 4
List class declarations:
auto list = new List <T, OBJECTS_PER_JSON_FILE>(); //Not used in this process
auto members = new List <NVPair <std::string, std::string>, DATA_MEMBERS_PER_OBJECT>();
Operator call:
n = trim(trim(getName(line)),'"'); //Trim removes extra characters from line and returns Name
v = trim(trim(getValue(line)),'"'); //or Value as a std::string
*members += NVPair<std::string, std::string>(n,v);
List Class
//List.h
#include <iostream>
#include <string>
template <typename T, unsigned int n>
class List{
T *Array[n];
size_t elements;
T dummy;
public:
List(){ elements = 0u; }
size_t size() const { return elements; }
const T& operator[](unsigned int i) const{...}
void operator+=(const T& add){ //adds a copy to the element
*Array[elements] = add;
elements++;
}
void operator+=(T&& add){ //moves element
*Array[elements] = std::move(add);
elements++;
}
Name Value pair Class
//NVPair.h
#include <iostream>
#include <string>
template <typename T, typename B>
class NVPair{
T Name;
B Value;
public:
NVPair(){ Name = ""; Value = ""; }
NVPair(T n, B v){ Name = n; Value = v; }
T name() const { return Name; }
B value() const{ return Value; }
NVPair& operator=(const NVPair& add){
Name = add.Name;
Value = add.Value;
return *this; }
NVPair& operator=( NVPair&& add){
Name = std::move(add.Name);
Value = std::move(add.Value);
return *this; }
};
I have been trying to debug it and it fails and goes into xstring at Name = std::move(add.Name) inside the NVPair =function.
Any help is much appreciated!
Edit: Looks like I was being very vague (Sorry first time). So The main program reads information from a file that contains Name and value pairs. It then creates a List object that is an array of pointers to Name value pair objects. So T * Array[n] is an array of T pointers. The purpose is to store information from a file that looks like this:
{
"Name" : "Cat"
"type" : "animal"
}
and so on...
The array member inside your List class is never initialized, but you are referencing it in the += operator. Also, I presume you want an array of NVPairs not array of NVPair pointers
Try doing something along the lines of:
template <typename T>
class List
{
T Array[n];
size_t size;
size_t elements;
T dummy;
public:
List(size_t maxSize) : size(maxSize), elements(0)
{
Array = new T[size];
}
void operator+=(T&& add)
{ //moves element
//This was where it was failing because Array was not defined
Array[elements] = std::move(add);
elements++;
}
//The rest of the class
}
The constructor will create an array of NVPairs, then you assign the values as normal. Notice that you need 2 variables, one for the size, one for the number of elements currently in the array.
I have tried looking around and tried all the solutions but I cannot seem to fix my problem. I know i get the segmentation fault on the push_front line but I am just lost. Here is the code-
#include <iostream>
#include <fstream>
#include <sstream>
#include <list>
using namespace std;
typedef std::list<int> hSlots; //the list
typedef hSlots* hTable; //an array of lists
class HashTable
{
private:
int p; //p=number of slots in the hash table
hTable tmpPtr;
hTable *table;
public:
HashTable(int p1);
int h1(int k);
~HashTable();
void chainedHashInsert(int x);
};
HashTable::HashTable(int p1)
{
p=p1;
hTable tTable[p];
//initializing to empty lists
for (int i=0; i<p; i++)
{
tmpPtr = new hSlots;
tTable[i] = tmpPtr;
}
table = tTable;
}
//destrcutor
HashTable::~HashTable()
{
delete table;
delete tmpPtr;
}
void HashTable::chainedHashInsert(int x)
{
tmpPtr = table[h1(x)];
cout<<"hashed"<<endl;
tmpPtr->push_front(x); //segmentation fault
}
int HashTable::h1(int k)
{
int z = k%p;
return z;
}
I have not used a lot of lists so I'm not too sure
Maybe this could be a proper answer after all.
Your problems arise from doing memory management (wrong) manually, when really there is no need to, in C++.
Here's my take on it using direct automatic memory management in C++:
#include <vector>
#include <list>
using namespace std;
template <typename T, typename hSlots = std::list<T> >
class HashTable
{
private:
int p; //p=number of slots in the hash table
std::vector<hSlots> table;
int getbucket(int k) { return k%p; }
public:
HashTable(int p1) : p(p1), table(p1) {}
void chainedHashInsert(int x)
{
auto& tmpPtr = table[getbucket(x)];
tmpPtr.push_front(x);
}
};
int main()
{
HashTable<int> table(37);
}
since tTable is local variable to HashTable, it disappears when HashTable method returns and leaves table as a dangling pointer.
So to get rid of that do as below; create a space for table using new.
HashTable::HashTable(int p1)
{
p=p1;
table = new ttTable[p];
//initializing to empty lists
for (int i=0; i<p; i++)
{
tmpPtr = new hSlots;
table[i] = tmpPtr;
}
}
table = tTable;
This line is the problem (or at least one of them).
You're stashing a pointer to an automatic object into a member variable, then dereferencing (and deleting!) it later, once the object has been destroyed.
I have a custom class as a key in a map. When I try to insert an item into the map, the program terminates. There has to be a problem with the creation of the key.
class MyKey {
char* data;
bool operator<(const MyKey& s) const {
for(int i = 0; i < (int)(sizeof(data)/sizeof(char)); i++) {
if(data[i] > s.data[i])
return false;
}
return true;
}
}
map<MyKey, char>* map = new map<MyKey, char>;
MyKey* key = new MyKey(...);
map->insert(make_pair(*key, '0'));
The program terminates at the insert.
You can't determine the size of an array from the pointer alone like you're attempting to-do in the for-loop of your operator< function ... You will have to, at some point, pass in the size of the array that is being pointed to by data so that you don't overflow the bounds of the array data is pointing to. Since data is a pointer, sizeof(data) simply returns the size of a pointer on your platform, not the size of the array being pointed to by data.
For C++, rather than using an allocated array, you should possibily use a STL container that you can directly query for the size of the container object ... this could include std::string if it's string-data, or std::vector<unsigned char> if it's just a bunch of binary bytes.
The following works and prints A.
#include <iostream>
#include <map>
using namespace std;
class Key
{
public:
Key(int x):data(x) {}
bool operator<(const Key &k) const { return(data < k.data); }
private:
int data;
};
int main()
{
Key myKey(10);
map<Key, char> m;
m.insert(make_pair(myKey, 'A'));
map<Key, char>::iterator it = m.find(myKey);
if (it != m.end())
{
cout << (*it).second << endl;
}
}
From your example code, the operator < would not be called because you only insert one element in the map. And you said you don't implement a copy constructor. So following code would be a problem:
class MyKey {
public:
MyKey()
{
data = new char[10];
}
~MyKey()
{
delete data;
}
private:
char* data;
};