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
Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top