0

Suppose I have this data structure in C++ :

struct Stash {
  int size;      // Size of each space
  int quantity;  // Number of storage spaces
  int next;      // Next empty space
   // Dynamically allocated array of bytes:
  unsigned char* storage;
  // Functions!
  void initialize(int size);
  void cleanup();
  int add(const void* element);
  void* fetch(int index);
  int count();
  void inflate(int increase);
};///:~

void Stash::initialize(int sz) {
  size = sz;
  quantity = 0;
  storage = 0;
  next = 0;
}

int Stash::add(const void* element) {
  if(next >= quantity) // Enough space left?
    inflate(increment);
  // Copy element into storage,
  // starting at next empty space:
  int startBytes = next * size;
  unsigned char* e = (unsigned char*)element;
  for(int i = 0; i < size; i++){
     storage[(startBytes + i)] = e[i];

  }

  next++;
  return(next - 1); // Index number
}

void* Stash::fetch(int index) {
  // Check index boundaries:
  assert(0 <= index);
  if(index >= next)
    return 0; // To indicate the end
  // Produce pointer to desired element:
  int value = (index*size);
  return &(storage[value]);
}

int Stash::count() {
  return next; // Number of elements in CStash
}

void Stash::inflate(int increase) {
  assert(increase > 0);
  int newQuantity = quantity + increase;
  int newBytes = newQuantity * size;
  int oldBytes = quantity * size;
  unsigned char* b = new unsigned char[newBytes];
  for(int i = 0; i < oldBytes; i++)
    b[i] = storage[i]; // Copy old to new
  delete []storage; // Old storage
  storage = b; // Point to new memory
  quantity = newQuantity;
}

void Stash::cleanup() {
  if(storage != 0) {
    cout << "freeing storage" << endl;
    delete []storage;
  }
}  ///:~

Suppose now I use the data structure to memorize strings in this way :

int main(){


    Stash* st1 = new Stash;

    st1->initialize(sizeof(string));
    string s1 = "This is a GOOD morning";
    st1->add(&s1);


    string s2 = "This is a BAD morning";
    st1->add(&s2);


    string* s3;
    s3 = static_cast<string*> (st1->fetch(0));
    cout << *s3 << endl;

    string* s3;
    s3 = static_cast<string*> (st1->fetch(1));
    cout << *s3 << endl;

    st1->cleanup();

    delete st1;

    return 0;
}

It Works!!! This is the output:

This is a GOOD morning
This is a BAD morning

But in this other way:

int main(){


    Stash* st1 = new Stash;

    st1->initialize(sizeof(string));
    string s1 = "This is a GOOD morning";
    st1->add(&s1);

    s1 = "This is a BAD morning";
    st1->add(&s1);

    string* s3;
    s3 = static_cast<string*> (st1->fetch(0));
    cout << *s3 << endl;

    string* s4;
    s4 = static_cast<string*> (st1->fetch(1));
    cout << *s4 << endl;

    st1->cleanup();

    delete st1;

    return 0;
}

It doesn't work. This is the output:

This is a BAD morning
This is a BAD morning

So, what happened in the machine when I try to use the same reference? I have tried with other datatypes and it works well.

2
  • string is std::string? Because if yes, I see a very evil abuse of this class... Commented Aug 28, 2014 at 10:27
  • yes... i'm learning c++. thanks a lot Commented Aug 28, 2014 at 13:05

3 Answers 3

1

The first use of s1 invokes the constructor:

string s1 = "This is a GOOD morning";

You then add the address of s1 to the Stash. Next you assign a new value to s1:

s1 = "This is a BAD morning";

This doesn't create a new string -- it invokes the assignment operator which replaces the same string object with a new value. You then save another copy of the address of s1:

st1->add(&s1);

If you look at the data in st1 then you'll see you have two copies of the same pointer, both pointing to s1. This is expected. In the first case you are storing pointers to two different objects which contain different values.

Sign up to request clarification or add additional context in comments.

2 Comments

but the Stash data structure seems to memorize bytes of the string and not it's address. In fact the program works well with other datatypes...so i don't know... :-(
Ah, just noticed that. The problem is that you are treating the pointer to the string object as if it was a char array. So you are storing the pointer to the string, not the array of characters.
0

Your code copies the bytes making up a std::string container (NOT the characters that are in the string data). This probably consists of a pointer to the string data, plus a size and capacity.

When you write s1 = "stuff", the std::string internally allocates new memory, so its previous internal pointer is now invalid.

Then you retrieve that previous internal pointer from your data structure and try to use it, causing undefined behaviour.

If your intent is to save the characters in the string then you need to add s1.c_str() instead of &s1.

If your intent is to store a copy of any object then you need to invoke the copy constructor to create a copy; not do a bitwise copy as you are currently doing. You could also invoke a move constructor or move assignment operator if your intent is to store the object and not leave the original object behind.

Comments

0

In your second approach, you have used address of s1 to be stored in stack. And you are not copying the contents within the stack, so when you change s1 to have a different content earlier content also changes, because you essentially pushed the pointer not the copy of the content. For example, if you do the following (copying the content to a new string to be used for push), this works:

string s1 = "This is a GOOD morning";
st1->add(new string(s1));

s1 = "This is a BAD morning";
st1->add(new string(s1));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.