2

I have the following code and it seems that to create a reference with new is okay, however when it comes to creating an object with new, it crashes when i try to recollect the memory allocated

float &f = *new float(1.3);
delete &f;
float f1 = *new float;
delete &f1;

I'd like to know the difference, thanks !

BRYAN
  • 51
  • 4
  • References are not pointers. –  Nov 10 '13 at 07:13
  • Thanks for the input, @H2CO3, but it seems the confusion here is that there are multiple operations involved. `new` allocated some memory and returns an address. E.g. you could have a line with `new float;` and it'd compile. The next part, `*new float` dereferences the pointer to obtain the value at that address. Assigning the value is another operation, which is unconnected to the `new` operator. The reference is initialized via the value (and its location), not `new`. – jozxyqk Nov 10 '13 at 07:24
  • @jozxyqk That is because **references are not pointers.** –  Nov 10 '13 at 07:27
  • http://stackoverflow.com/a/3954803/1888983 – jozxyqk Nov 10 '13 at 07:36
  • I am curious how this is legally compiled... Isn't `*new something` an rvalue? How can you bind a rvalue to a lvalue reference??? – SwiftMango Nov 11 '13 at 07:14
  • @texasbruce Why would it be an rvalue? Dereferencing a pointer is lvalue, is not it? See http://stackoverflow.com/questions/4773839/is-a-dereferenced-pointer-a-valid-lvalue – Suma Nov 11 '13 at 08:20
  • @Suma Even the pointer itself is an rvalue (that is directly returned from a function)? So we have rvalue -> lvalue transition? – SwiftMango Nov 11 '13 at 08:24
  • 1
    @texasbruce `*` dereferences a pointer, regardless of r/lvalue. E.g. `*ptr = *otherPtr;` to copy the values. This also compiles `*new float = *new float(1.3f);`. – jozxyqk Nov 11 '13 at 10:23

2 Answers2

1

Consider:

float f1 = *new float;

It creates a object of type float on free store and then copies the original object to f1. You lose the address of the original free store object, this gives you a instant memory leak.

All memory de-allocation functions mandate that address being passed to them should be same as the one returned by memory allocation functions, so you end up with an undefined behavior when you call delete on a address not returned through new.

Note that an Undefined behavior doesn't mandate a crash, but absence of a crash doesn't mean there ir no problem in the code.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • In regard to the first part, it looks like `&f` will produce the same address as returned by `new`. Is this guaranteed? (not saying this should be done, just interested in the way references are implemented) – jozxyqk Nov 10 '13 at 07:17
  • @jozxyqk: It is not the same. Either case you end up losing the address to the original object. – Alok Save Nov 10 '13 at 07:24
  • So you're saying that after `float &f = *new float(1.3);`, `&f` will return a different address? I think it returns the *same* address, just not certain it is guaranteed. – jozxyqk Nov 10 '13 at 07:25
  • `float *p = new float; float &f = *p; printf("%p %p\n", p, &f);` gives `0x1679010 0x1679010`. – jozxyqk Nov 10 '13 at 07:32
  • @jozxyqk: They are not equivalent, You can see the difference in the another answer posted here. – Alok Save Nov 10 '13 at 07:38
  • Well, I compiled `float *f = new float; delete f;` and `float &f = *new float; delete &f;`. Both produce the same assembly output and `valgrind` reports no leaks. In practice, with `g++` they are identical in implementation, though I can't be certain the same would happen with different compilers. – jozxyqk Nov 10 '13 at 07:45
0

Firstly, "creating a reference with new" isn't exactly what you're doing. To understand this, the code needs to be broken up...

new float(1.3);

this allocates 4 bytes of memory for a float, constructs the float using a 1.3 double constant, and returns a pointer to the start of the memory - the first of the 4 bytes.

*new float(1.3);

this "dereferences" the pointer returned by the new operator, so instead of reading the pointer value, you're reading the 4-byte float data it points to.

float &f = *new float(1.3);

constructs a reference f, using the float provided by new. While (AFAIK) references are implemented as pointers with greater potential for compiler optimizations (?), this is one part people are complaining about. Conceptually, the reference is the float and you shouldn't be assuming you can retrieve the address, free the memory and hence invalidate the reference later.

However, as you have found, it does actually work and you can retrieve the address...

&f

produces a pointer to the memory at the reference's location, which you can then

delete &f;

To answer your actual question...

float f1 = *new float;

can be rewritten as follows

float f1; //a float on the stack!
f1 = *new float; //should probably be: *new float(1.3)

which does not initialize a reference, but instead copies the data allocated by new to the float on the stack. After the copy, the memory address returned by new is lost forever - you have "leaked" memory.

Now on to why it crashes.

&f1;

creates a pointer to the float f1, on the stack. This memory has not been created by new or the memory allocation library new ultimately uses. Attempting to free a memory address that doesn't exist in the memory allocation library...

delete &f1;

...results in your crash.

The difference between float &f and float f1: one is a reference, internally implemented as a pointer (which is assumed never to change) and one is an actual float variable declared on the stack which does not reference other memory.

What you should be doing instead is

float *f = new float(1.3f);
// access f as *f = 1.2f or cout << *f
delete f;
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • My original understanding of why the delete &f crashes is it illegally retrieves the memory that is allocated out of Stack using delete which is supposed to retrieve memory allocated only from the heap. After your cogent explanation, my thought is justified ! Thanks! – BRYAN Nov 11 '13 at 09:48