C++ map insert with custom key fails - c++

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;
};

Related

Checking whether a pointer has been set to an initialized object

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.

End of array points to newly allocated array C++

Using C++, I am trying to create an array that holds pointers to objects I'm storing. But when the array is full, I want to expand the array.
the easy option is to allocate a new array with bigger size, then copy the elements to it, this is quite inefficient, and I thought of another way I want to try to do it:
create array of fixed size X
When full, create a new array, and make the end of the first array point to the start of the first element
Repeat as long as needed
What methods can I use to do that? I thought of one way to do it, but it seems very hacky:
declare all my new array as pointers to object pointer, then reinterprit_cast the filled elements to object pointer.
Note: I know I can use Vector, but I am instructed not to use std library.
Kind Regards,
There are some good answers in the comments already. I just want to provide a way to achieve exactly the behavior you described.
Since the elements of the array are pointers as well, you can define a union as the element of your array like this:
template<typename T>
union Cell
{
T* pElm;
Cell* pNext;//A fixed size array of Cells
}
And then build your array on top of it. For example:
template<typename T>
class SpecialArray
{
public:
//the next pointer is included
static const size_t ARRAY_LEN = 1000;// For example
using Pointer = T*;
using Segment = Cell<T>[ARRAY_LEN];
protected:
Segment* pFirst;
size_t mSize;
public:
SpecialArray()
:pFirst(nullptr),mSize(0){}
SpecialArray(SpecialArray&&){}
~SpecialArray(){}
Pointer& operator[](size_t index)
{
Segment* seg = pFirst;
size_t offest = 0;
//Search logic...
return seg[offest]->pElm;
}
const Pointer& operator[](size_t index) const;
};
Using C++, I am trying to create an array that holds pointers to
objects I'm storing. But when the array is full, I want to expand the
array.
With C++ templates and C primitives we can improvise a simple vector like below. And the grow buffer strategy is to double the size when the threshold is met.
#include <iostream>
#include <stdlib.h>
template <typename T>
class MyVector
{
public:
MyVector() : m_count(0), m_size(0), m_buffer(0)
{
m_size = bufInitSize;
m_buffer = (T*)malloc(sizeof(T) * bufInitSize);
}
~MyVector()
{
if (m_buffer)
free(m_buffer);
}
void add(const T& p)
{
if (m_count + 1 >= m_size)
{
m_size *= 2;
m_buffer = (T*)realloc(m_buffer, sizeof(T) * m_size);
}
m_buffer[m_count ++ ] = p;
}
T& operator[](int idx)
{
return m_buffer[idx];
}
private:
static const int bufInitSize = 1024;
T* m_buffer;
int m_count;
int m_size;
};
void main()
{
// using MyVector
MyVector<int*> vctOfIntPtr;
int n = 100;
vctOfIntPtr.add(&n);
int* pN = vctOfIntPtr[0];
std::cout << *pN;
}

Creating [][] operator for class in c++

I am making a Pentago game for someone, and I wanted to write a good code, so I decided to use operator overloading.
We have 2 classes; first one is Block class (which represents every single block of the board) and the second one is Set class (which represents a 3*3 table of blocks).
Now I want to use Set as a 2d array so I can use set[foo][foo].
Can you help me to make an operator like this?
A very simple solution is
struct MyClass {
int x[3][3];
int* operator[](int row) { return &(x[row][0]); }
};
i.e. returning an element* from operator[].
This allows using
myinstance[row][col]
There are (at least) two ways to go here.
The first is to make something like a set_row class, which is a proxy. So you'd have something like
class set
{
public:
set_row operator[](size_t row)
{
// Return a proxy object that just sees the correct row.
return set_row(internal_buffer_pointer[row]);
}
...
};
where set_row is something like
class set_row
{
public:
// Ctor takes a row
// Take a column, and return a reference to the correct column in the row.
element &operator[](size_t column);
};
From experience (on, ahem, VisualC++), this was slow, as it will need to construct a proxy object for each access.
The second is to forgo operator[], and use operator():
class set
{
public:
element &operator()(size_t row, size_t col);
...
};
It would be nice using operator[], but, unfortunately, you can't do that with it.
There is no operator[][]. If you want to provide those semantics you need to overload operator[] such that it returns another object that also overloads operator[].
Your case can be solved using a vector of vectors:
#include <vector>
#include <cstdint>
#include <iostream>
struct Block
{
int value = 0;
};
class Set
{
std::vector<std::vector<Block> > grid;
public:
Set(): grid(3, std::vector<Block>(3)) {} // 3 x 3
std::vector<Block>& operator[](std::size_t x) { return grid[x]; }
};
int main()
{
using std::size_t;
Set set;
set[1][1].value = 1;
for(size_t x = 0; x < 3; ++x)
{
for(size_t y = 0; y < 3; ++y)
{
std::cout << set[x][y].value << ' ';
}
std::cout << '\n';
}
}
Output:
0 0 0
0 1 0
0 0 0
This works because Set::operator[] returns reference to a std::vector and the std::vector overloads operator[] to return a reference to a Block.
There is no way to supply an operator[][] for a class.
However, if your Set supplies an operator[](), that operator can return a reference to something else that also has an operator[]().
For example;
class Row
{
public:
Block &operator[](int block_no) {return data[block_no];};
private:
std::vector<Block> data;
};
class Set
{
public:
Row &operator[](int row_no) {return row[row_no];};
private:
std::vector<Row> row;
};
int main()
{
Set s;
// assume s is set up appropriately
Block b = s[2][3]; // equivalent to s.operator[](2).operator[](3)
}
Obviously, it is also necessary to do relevant error checking, set up the contents of the classes correctly, etc.
Assuming the memory is contiguous, you can return a pointer to the first element of the row.
Working example
#include <iostream>
class MyType
{
public:
static const size_t rows = 3;
static const size_t columns = 3;
static const size_t size = rows * columns;
MyType()
{
for(size_t index = 0; index < 9; ++index)
{
data[index] = index;
}
}
int* operator[](size_t index)
{
return &data[rows * index];
}
private:
int data[size];
};
int main()
{
MyType instance;
std::cout << instance[2][1] << std::endl;
}

Missing nullptr for undeclared array element

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

R-value overloaded Operator string assignment error

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.