1

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
sealskej
  • 7,281
  • 12
  • 53
  • 64

6 Answers6

2

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.

Vyktor
  • 20,559
  • 6
  • 64
  • 96
  • 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` – sealskej Jan 31 '12 at 00:39
  • 1
    @sealskej add this warning to your question and add output of `bt` (backtrace) command from gdb after fall (I hope you're using -g flag when compiling). – Vyktor Jan 31 '12 at 00:41
  • Thanks for your help. I added `bt` to my post as you have suggested. – sealskej Jan 31 '12 at 01:03
  • 1
    @sealskej I still don't see bt output, it should be something like: `#0 XS::ModuleLoader::i () at include/ModuleLoader.h:35 #1 0x000000000040e483 in main (argc=1, argv=0x7fffffffe628) at src/main.cpp:37` – Vyktor Jan 31 '12 at 01:12
  • Okey. I am trying to find it in XCode. – sealskej Jan 31 '12 at 01:27
  • 1
    @sealskej if you have console available you should be able to run: `gdb yourApp` than type `run` (inside gdb) and when application falls than `bt` :) – Vyktor Jan 31 '12 at 01:29
  • Okey. Thanks. Edited now in my post. – sealskej Jan 31 '12 at 02:25
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.

Community
  • 1
  • 1
VinGarcia
  • 1,015
  • 12
  • 19
0

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.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
0

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];
  }
};
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I have tried this, but I am getting same backtrace mentioned in EDIT part of my post. Using `CEntry *operator=( CEntry *orig)`. – sealskej Jan 31 '12 at 00:59
  • @sealskej: Get rid of `CEntry`'s assignment operator, the compiler-generated one should be fine. And then try this code. – Ben Voigt Jan 31 '12 at 01:03
  • When I get rid of `CEntry`'s assignment operator, I am getting `No viable overloaded '='` by XCode on line `a[1] = new CEntry(CKey(1), CValue(1));` – sealskej Jan 31 '12 at 01:07
  • @sealskej: Line 164 and 168 are not the same as what I gave you. – Ben Voigt Jan 31 '12 at 01:19
0

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.

John
  • 6,433
  • 7
  • 47
  • 82
  • Yeah, this could be solution, but I would have to create getter as well, I guess. – sealskej Jan 31 '12 at 01:02
  • I was expecting a way to do it like the std::vector class does, e.g.: myVec[10]=value; None of the available answers explained that. Is it not possible? – VinGarcia Mar 01 '16 at 01:50
0

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];
};
John3136
  • 28,809
  • 4
  • 51
  • 69
  • 1
    No, this will just happily write past the end of the `entries` array and trash other objects' memory. – Ben Voigt Jan 31 '12 at 01:05
  • Yes, you are 100% correct. Stupid stupid stupid stupid. Allocated the objects, but didn't increase the array size to include them. Makes me think if what the OP wants is a random access 1 base array, might be better to wrap a vector or something. – John3136 Jan 31 '12 at 02:03
  • I definitely agree. But everyone has to rewrite `std::vector` at least once so they properly appreciate it. – Ben Voigt Jan 31 '12 at 02:42
  • I cant use `std::vector`, because I am doing school homework, where it is forbidden. – sealskej Jan 31 '12 at 02:54
  • Maybe I will use John's solution, but overloaded subscript operator seemed more elegant for me. I need to create 1 base array, because I want to rewrite b-tree operations pseudocode, which uses 1 base array. – sealskej Jan 31 '12 at 03:10
  • overloading will work, but you need to address my actual point in the above. Since you want to say a[1] = , you need to be sure that a[1] works even on an empty collection. – John3136 Jan 31 '12 at 04:27