Assign value using overloaded subscript operator
-
05-12-2019 - |
Question
I am trying to create custom array indexed from 1 using subscript operator. Getting value works fine, but I have no clue, why assign using subscript operator doesn't work.
class CEntry {
public:
CKey key;
CValue val;
CEntry(const CKey& key, const CValue& val) {
this->key = key;
this->val = val;
}
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
};
...
class EntriesArray {
public:
CEntry **entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry*[length];
int i;
for (i = 0; i < length + 1; i++) {
entries[i] = NULL;
}
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
};
Constructs array this way
EntriesArray a(5);
This works
a.entries[0] = new CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
This doesn't work
a[1] = new CEntry(CKey(1), CValue(1));
EDIT:
Using
CEntry *operator=( CEntry *orig)
it compiles okey, but gdb stops at
No memory available to program now: unsafe to call malloc warning: Unable to restore previously selected frame
with backtrace
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5f3ffff8
0x00000001000013c8 in CEntry::operator= (this=0x0, orig=0x1001008d0) at /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp:20
20 /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp: No such file or directory.
in /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp
Solution
You could try replacing
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
with
void Add(const int index, CEntry *pEntry) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
entries[index - 1] = pEntry;
};
but since you are now storing references to objects allocated on the heap (with new) you will need a destructor ~EntriesArray()
to delete them all.
OTHER TIPS
At first... This:
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
Shouldn't work (this should result in recursive call of operator=
).
The second thing is that you're trying to assign CEntry *
to CEntry
, this would work if you had CEntry *operator=( CEntry *orig)
, but I think this is bad coding practice.
Because EntriesArray::operator[]
returns a CEntry &
, but new CEntry
returns a CEntry *
.
Perhaps you want a[1] = CEntry(CKey(1), CValue(1))
? (no new
.)
By the way, your current definition of CEntry::operator=
will lead to a stack overflow.
This
return *entries[index - 1];
dereferences a NULL pointer.
You want the pointer itself to be overwritten by a[1] = new CEntry(CKey(1), CValue(1));
, not the pointed-to-value.
Try this:
class EntriesArray
{
public:
int length;
CEntry **entries;
EntriesArray( int length ) : length(length), entries(new CEntry*[length]())
{
}
// defaulted special member functions are inappropriate for this class
EntriesArray( const EntriesArray& ); // need custom copy-constructor
~EntriesArray(); // need custom destructor
EntriesArray& operator=(const EntriesArray&); // need custom assignment-operator
CEntry*& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return entries[index - 1];
}
};
Further to my comment above: To make it work with writing new values, you probably need something like this (I haven't double checked for off by one or ptr vs reference stuff)
CEntry& operator[] (const int index) {
if (index < 1) {
throw ArrayOutOfBounds();
}
// Add default elements between the current end of the list and the
// non existent entry we just selected.
//
for(int i = length; i < index; i++)
{
// BUG is here.
// We don't actually know how "entries" was allocated, so we can't
// assume we can just add to it.
// We'd need to try to resize entries before coming into this loop.
// (anyone remember realloc()? ;-)
entries[i] = new CEntry();
}
return *entries[index - 1];
};
This question may be related to this one.
I tried to fix your code; I believe that this is what you were trying to do:
(tested this code on g++ 5.3.0)
#include <iostream>
#include <stdexcept>
#include <string>
// Some implementation for CKey and CValue:
typedef int CKey;
struct CValue {
int value;
CValue(int value=0) : value(value) {}
};
class CEntry {
public:
CKey key;
CValue val;
CEntry(): key(0), val(0) {}
CEntry(const CKey& key, const CValue& val): key(key), val(val) {}
CEntry& operator= (const CEntry& b) {
this->key = b.key;
this->val = b.val;
return *this;
};
};
class EntriesArray {
public:
CEntry *entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry[length];
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw std::domain_error("out of bounds!");
}
return entries[index - 1];
};
};
int main(int argc, char* argv[]) {
using namespace std;
EntriesArray a(5);
// This works
a.entries[0] = CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
// This doesn't work
a[1] = CEntry(CKey(2), CValue(2));
cout << a[1].val.value << endl;
}
Also you might want to use a[1]
as a[1].val.value
e.g.:
cout << a[1] << endl;
To do this just add to this line to cEntry:
operator int() { return val.value; }
I hope it helps.