Let's say I have a class defined like this:
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
class Buffer
{
public:
Buffer(uint16_t bytes)
{
ptr = malloc(size = bytes);
}
~Buffer()
{
free(ptr);
}
inline char* str() { return (char*)ptr; }
inline int length() { return strlen(str()); }
void* ptr;
uint16_t size;
};
Obviously the simplest possible buffer.
When I create a variable of that type, the constructor is called and the memory is allocated for data. When the variable goes out of scope, the destructor is called and the memory is freed.
Then I made a function, that writes something to that buffer:
writeSomething(Buffer buffer) // note that the Buffer type is not marked as reference
{
// writes something.
}
And I have some code that writes something else...
test()
{
Buffer text(1024);
writeSomething(text);
doSomethingElse(text.ptr);
}
All hell breaks loose. When I tested the code with the debugger, I noticed that Buffer
destructor is called from a line containing doSomethingElse(text.ptr)
, that makes the pointer invalid and causes unexpected behavior, like next malloc() allocates the address that was already allocated by the text
instance.
Just having a hunch, I changed the writeSomething()
function signature to:
writeSomething(Buffer& buffer); // note marking Buffer type as reference
That solved the problem, but I still don't understand why. What happens when the argument is not marked as reference? I thought it was obvious it's treated as reference, since it's not a primitive type. As I see in the debugger, something really weird is happening.
So what does the first signature mean? What is passed as the argument? If it's not the reference to the class instance, then what is it?
Why is the destructor called there?