Reseting object m_object = Object(new, parameters); - c++

I have a simple class:
class Histogram {
int m_width;
int m_height;
int m_sampleSize;
int m_bufferWidth;
int m_bufferHeight;
uint8* m_buffer;
int m_size;
public:
Histogram() : m_buffer(0) { }
Histogram(int width, int height, int sampleSize) {
m_buffer = new unsigned char [width*height*sampleSize];
}
~Histogram() {
my_log("destructor: buffer: %p", m_buffer);
if ( m_buffer ) { delete [] m_buffer; m_buffer = NULL; }
}
unsigned char* buffer() {
return m_buffer;
}
};
It is a member in other class:
class Other {
Histogram m_histogram;
void reset() {
my_log("reset() called: buffer: %p", m_histogram.buffer());
m_histogram = Histogram(512, 512, 2);
}
}
Now, I first create "uninitialized" object using Histogram() constructor – which sets m_buffer to NULL;
Then, I call the reset method, which does m_histogram = Histogram( 512, 512, 3 ) – the new object has m_buffer initialized via new.
So expected sequence of log messages is:
"reset() called: buffer: 0x0"
"destructor: buffer: 0x0"
But instead, I get:
"reset() called: buffer: 0x0"
"destructor: buffer: 0x072a7de"
So some irrational action is being performed. Moreover, the 0x072a7de address is displayied when I also delete the second object (created with "larger" constructor, with three int parameters).

You MUST realize copy-ctor and assignment operator for your class Histogram, since
m_histogram = Histogram(512, 512, 2);
is assignment operator call. Implicit operator = bitwise copy members of your class.
And you must use delete[] in destructor, not delete, since you allocate an array.

First of all, since you are pointing to a dynamically allocated array, you need to use operator delete[]
delete[] m_buffer;
Second, and more importantly, since you have dynamically allocated memory, you should follow the rule of three and implement a copy constructor, and assignment operator, as well as fixing the destructor.
What happens now is that your (compiler synthesized) assignment operator is making a "shallow" copy, i.e. it is copying the pointer. Then you will hve multiple destructors trying to delete it. You are invoking undefined behaviour.
You could really save yourself a lot of trouble by using an std::vector<uint8> as a buffer.

Related

I can't assign an object to an object array

I just wrote my first copy constructor and copy operator, and I'm trying to assign an object instance to an array like so:
Agent agent = Agent(navmesh, rb, m_maxPathSize);
Agent tmp = agent; // DEBUG
m_agents[idx] = agent;
The copy constructor seems to be working fine, since tmp is a perfect copy of agent (with a newly assigned m_path pointer). But when I assign agent to m_agents[idx], the latter consists of what I'd expect from the default constructor (m_path == 0, m_alive == false).
My constructors look like this:
Agent() { m_path = 0; m_alive = false; };
Agent::Agent(NavMeshNavigator* navmesh, RigidBody* rb, int maxPathSize)
: m_rb(rb), m_navmesh(navmesh), m_maxPathCount(maxPathSize)
{
m_path = new float3[maxPathSize];
};
Agent::Agent(const Agent &a)
{
memcpy(this, &a, sizeof(Agent));
if (m_path)
{
float3* oldptr = m_path;
m_path = new float3[m_maxPathCount];
memcpy(m_path, oldptr, m_maxPathCount * sizeof(float3));
}
}
Agent& Agent::operator=(const Agent &a) { return Agent(a); }
Agent::~Agent() { if (m_path) delete[] m_path; };
...
protected:
float3* m_path;
bool m_alive = true;
The constructor allocates memory for m_path using new[], the destructor frees it with delete[], the copy operator calls the copy constructor, and the copy constructor first memcopies the original before allocating a new m_path array.
In my test case, idx == 0, so that can't be it. I originally used malloc instead of new[] but got the same results. I'd say the problem is in my copy constructor/operator since I have no experience with that, but then why does it work perfectly on tmp?
EDIT:
The m_agents array is declared and destroyed like this:
NavMeshAgents(int maxAgents, int maxAgentPathSize)
: m_maxAgents(maxAgents), m_maxPathSize(maxAgentPathSize)
{
m_agents = new Agent[maxAgents];
};
~NavMeshAgents() { if (m_agents) delete[] m_agents; m_agents = 0; };
As #Evg #HolyBlackCat and #Adrian-Reinstate-Monica explained in the comments, new[] calls the default constructor for all its members. Agent tmp = agent calls the copy constructor, whereas tmp = agent would've called the assignment operator (tmp.operator=(agent)). My assignment operator was wrong, it should initialize this (and then return *this rather than return an instance.

Placement New on already existing object in SharedMemory

I have two programs. The first allocates a Shared-Memory file and the second reads from it.. I am using placement-new to place objects into this memory guaranteeing that the objects do NOT use new or allocate any memory outside of the Shared-Memory file.
My Array structure:
template<typename T, size_t Size>
struct SHMArray {
SHMArray() : ptr(elements) {}
SHMArray(const SHMArray& other) { std::copy(other.begin(), other.end(), begin()); }
SHMArray(SHMArray&& other)
{
std::swap(other.ptr, ptr);
std::fill_n(ptr.get(), Size, T());
}
~SHMArray()
{
std::fill_n(ptr.get(), Size, T());
}
constexpr bool empty() const noexcept
{
return Size == 0;
}
constexpr size_type size() const noexcept
{
return Size;
}
T& operator[](std::size_t pos)
{
return *(ptr.get() + pos);
}
constexpr const T& operator[](std::size_t pos) const
{
return *(ptr.get() + pos);
}
T* data() noexcept
{
return ptr.get();
}
constexpr const T* data() const noexcept
{
return ptr.get();
}
private:
offset_ptr<T> ptr;
T elements[];
};
Program 1:
int main()
{
//Allocate a shared memory file of 1mb..
auto memory_map = SharedMemoryFile("./memory.map", 1024 * 1024, std::ios::in | std::ios::out);
memory_map.lock();
//Pointer to the shared memory.
void* data = memory_map.data();
//Place the object in the memory..
SHMArray<int, 3>* array = ::new(data) SHMArray<int, 3>();
(*array)[0] = 500;
(*array)[1] = 300;
(*array)[2] = 200;
memory_map.unlock(); //signals other program it's okay to read..
}
Program 2:
int main()
{
//Open the file..
auto memory_map = SharedMemoryFile("./memory.map", 1024 * 1024, std::ios::in | std::ios::out);
memory_map.lock();
//Pointer to the shared memory.
void* data = memory_map.data();
//Place the object in the memory..
//I already understand that I could just cast the `data` to an SHMArray..
SHMArray<int, 3>* array = ::new(data) SHMArray<int, 3>();
for (int i = 0; i < array.size(); ++i)
{
std::cout<<(*array)[i]<<"\n";
}
memory_map.unlock(); //signals other program it's okay to read..
}
Program One placed the SHMArray in memory with placement new. Program Two does the same thing on top of program one's already placed object (overwriting it). Is this undefined behaviour? I don't think it is but I want to confirm.
Neither program calls the destructor array->~SHMVEC(); I also don't think this leaks as long as I close the MemoryMapped file then it should all be fine.. but I want to make sure this is fine. If I ran the programs again on the same file, it shouldn't be a problem.
I am essentially making the assumption that placement new is working as if I placed a C struct in memory in this particular scenario via: struct SHMArray* array = (struct SHMArray*)data;.. Is this correct?
I am essentially making the assumption that placement new is working
as if I placed a C struct in memory in this particular scenario via:
struct SHMArray* array = (struct SHMArray*)data;.. Is this correct?
No, this is not correct. Placement new also invokes the object's appropriate constructor. "struct SHMArray* array = (struct SHMArray*)data;" does not invoke any object's constructor. It's just a pointer conversion cast. Which does not invoke anyone's constructor. Key difference.
In your sample code, you do actually want to invoke the templated object's constructor. Although the shown example has other issues, as already mentioned in the comments, this does appear to be what needs to be done in this particular situation.
But insofar as the equivalent of placement new versus a pointer cast, no they're not the same. One invokes a constructor, one does not. new always invokes the constructor, whether it's placement new, or not. This is a very important detail, that's not to be overlooked.

`delete[]` on `int*` allocated with `new` gives malloc error

Following my understanding of C++ convention, I have:
class BlockRepresentation : public FPRepresentation
{
private:
class Block
{
public:
int id;
int fpDimensions;
int* position; // pointers in question
int* blockDimensions; // pointers in question
~Block();
};
std::vector<Block> all_blocks;
public:
BlockRepresentation( int count, int dimensions, int volumn[] );
void AddBlock( int id, int position[], int dimensions[] );
std::string ToGPL();
};
where new blocks are created in AddBlock:
void BlockRepresentation::AddBlock( int id, int position[],
int dimensions[] )
{
Block newBlock;
newBlock.id = id;
newBlock.fpDimensions = fpDimensions;
newBlock.position = new int[fpDimensions]; // pointers in question
newBlock.blockDimensions = new int[fpDimensions]; // pointers in question
for (int i = 0; i < fpDimensions; ++i)
{
newBlock.position[i] = position[i];
newBlock.blockDimensions[i] = dimensions[i];
}
all_blocks.push_back( newBlock );
}
so I have the following destructor:
BlockRepresentation::Block::~Block()
{
delete[] position;
delete[] blockDimensions;
}
but then I get:
rep_tests(11039,0x7fff71390000) malloc: *** error for object 0x7fe4fad00240: pointer being freed was not allocated
Why should I not delete[] the 2 pointers here?
As was pointed out in the comments, you violated the rule of three, and the violation is very obvious:
{
Block newBlock;
// snip
all_blocks.push_back( newBlock );
}
When this function returns, the newBlock object goes out of scope, and its destructor will delete all the newed arrays.
But you push_back()ed this object. This constructs a copy of the object into the vector. Because your Block does not define a copy constructor, the default copy-constructor simply makes a copy of all the pointers to the newed arrays.
If you somehow manage to avoid dereferencing the no-longer valid pointers, or you survived that experience, you're not of the woods yet. That's because, when the vector gets destroyed, and its Blocks get destroyed, their destructors will, once again, attempt to delete the same newed arrays that were already deleted once before.
Instant crash.
There is nothing wrong with your Block destructor. It is doing its job, which is releasing the memory that is pointed to by your two int * member variables. The problem is that the destructor is being called on the same pointer value multiple times, which results in a double-free error.
The entity that causes this is the std::vector<Block>, since a std::vector will make copies of your Block object, and your Block object is not safely copyable.
Since the member variables of Block that are pointers are position and blockDimensions, the most painless way to alleviate this issue is to use std::vector<int> instead of int *, as demonstrated by this sample program.
However, if you really wanted to use int *, you would need to implement a user-defined copy constructor. In addition, a user-defined assignment operator would complement the copy constructor. This is what is called the Rule of Three.
#include <algorithm>
//...
class Block
{
public:
int id;
int fpDimensions;
int *position;
int *blockDimensions;
Block() : position(nullptr), blockDimensions(nullptr),
id(0), fpDimensions(0) {}
~Block()
{
delete [] position;
delete [] blockDimensions;
}
Block(const Block& rhs) : id(rhs.id), fpDimensions(rhs.fpDimensions),
position(new int[rhs.fpDimensions]),
blockDimensions(new int[rhs.fpDimensions])
{
std::copy(rhs.position, rhs.position + fpDimensions, position);
std::copy(rhs.blockDimensions, rhs.blockDimensions + fpDimensions,
blockDimensions);
}
Block& operator=(const Block& rhs)
{
Block temp(rhs);
std::swap(temp.position, position);
std::swap(temp.blockDimensions, blockDimensions);
std::swap(temp.id, id);
std::swap(temp.fpDimensions, fpDimensions);
return *this;
}
};
See the live sample here.
See all of the hoops we had to jump through to get the Block class to behave correctly when used within a std::vector, as opposed to simply using std::vector<int>?

segmentation fault with deep copy

I am trying to make a deep copy (for copy on write) of an object but I get a segmentation fault.
I am using a hashtable with linked list.
class Person
{
public:
Person(const char * id,int nb)
{
this->id=strdup(id);
this->nb=nb;
this->init=init;
this->next=NULL;
}
Person(const Person& rhs) :
nb(rhs.nb),
init(rhs.init),
id(strdup(rhs.id)),
next(rhs.next == NULL ? NULL : new Person(*rhs.next)) {}
char* strdup(char const* in)
{
char* ret = new char[strlen(in)+1];
strcpy(ret, in);
return ret;
}
int nb,init;
const char * id;
Person *next;
};
Hashtable deepcopy (const Hashtable& rhs)
{
num[0]=num[0]-1;
Person** array=rhs.table;
Hashtable autre;
for (int i = 0 ; i < size; ++i)
if (autre.table[i]!=NULL)
autre.table[i] = new Person(*array[i]);
return autre;
num[0]=1;
}
the attributs of my class Hashtable:
Person **table;
int* num;
EDIT: this problem seem to be fixed.
What is wrong with my deep copy? I don't understand. I think that my copy constructor is good but I don't understand why I get a seg fault when I run it.
This code must be fixed:
for (int i = 0 ; i < size; ++i)
autre.table[i] = new Person(*array[i]);
table has fixed size, and it's filled with null-pointers. In your loop, you don't check if the element to be copied is a null-pointer, so you derefence it and try to copy the entity which even doesn't exist.
for (int i = 0 ; i < size; ++i) {
if(array[i] != NULL) {
autre.table[i] = new Person(*array[i]);
}
}
PS: It's better to use nullptr instead of NULL.
Problems that I see:
Default constructor of Person.
Person(const char * id,int nb)
{
this->id=id;
this->next=NULL;
}
If I use
Person foo()
{
char id[] = "John";
return Person(id, 0);
}
Person a = foo();
Then the stack memory used for holding "John" in foo is now held on to by a, which will lead to undefined behavior.
You need to take ownership of the input string. Use std::string for id instead of char const*.
Copy constructor of Person.
The statement
id(rhs.id),
will be a problem if you decide to use char const* as type for id. If you switch it to std::string, it won't be a problem.
Copy constructor of HashTable makes a shallow copy of table. This will be a problem if you decide to delete the table in the destructor of HashTable. If you don't delete table in the destructor of HashTable, you have a memory leak.
In deepcopy, you are not checking whether array[i] is NULL before dereferencing it. This has already been pointed out by #alphashooter. Additionally, you are creating a deep copy in a local variable of the function,autre. The deep copy is not visible outside the function unless you return autre from it.
EDIT
Since you are not allowed, to use std::string, you will have to allocate memory for the char const* in the default constructor as well as the copy constructor. If your platform has the non-standard function strdup and you are allowed to use it, you can change the default constructor to:
Person(const char * id,int nb)
{
this->id=strdup(id);
this->next=NULL;
}
You need to make a similar change to the copy constructor.
If you don't have strdup or you are not allowed to use it, you can define it. It's a very simple function to write.
char* strdup(char const* in)
{
char* ret = new char[strlen(in)+1];
strcpy(ret, in);
return ret;
}

Double-free error in execution after deleting pointer in deconstructor

I have a class containing a member pointer which is dynamically allocated in its constructor as follows:
class Record {
public:
Record(unsigned short numBytes, char* bufRecord);
~Record();
unsigned short size() {return m_numBytes;}
private:
unsigned short m_numBytes;
char* m_bufRecord;
};
Record::Record(unsigned short numBytes, char* bufRecord) {
m_numBytes = numBytes;
m_bufRecord = new char[numBytes];
for(unsigned short i=0; i<numBytes; i++)
m_bufRecord[i] = bufRecord[i];
}
Record::~Record() {
delete m_bufRecord;
}
It basically copies the input buffer into the dynamically allocated member buffer. I proceed to use this class as follows, in the constructor of another class:
class File {
public:
File(const char* fileName);
~File();
unsigned int numRecords() {return m_records.size();}
Record getRecord(unsigned int numRecord) {return m_gdsRecords[numRecord];}
private:
std::ifstream m_file;
std::vector<Record> m_records;
};
File::File(const char* fileName) : m_file(fileName, ios::in | ios::binary) {
while(!m_file.eof()) {
char bufNumBytes[2];
char* bufRecord;
unsigned short numBytes;
m_file.read(bufNumBytes, 2);
numBytes = (bufNumBytes[0] << 8) + bufNumBytes[1] - 2;
bufRecord = new char[numBytes];
m_file.read(bufRecord, numBytes);
Record record(numBytes, bufRecord);
m_records.push_back(record);
delete bufRecord;
}
}
However, when I instantiate this class, I get the following error, which seems to state that I'm double-freeing the m_bufRecord:
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001cb3280 ***
I'm guessing the problem lies with the insertion of a class containing a pointer to the vector element, and the destructor being called twice on the same pointer but I'm not sure how this happens. What am I doing wrong here?
This is a case of the Rule of three. If your class needs to free resources in the destructor, it generally needs to declare a copy constructor (and copy assignment operator), to either copy the owned resource, manage shared ownership or prevent being copied.
Record getRecord(unsigned int numRecord) {return m_gdsRecords[numRecord];}
This function returns a copy of the Record. So now you have two Records containing the same m_bufRecord pointer. Running the destructors on these Records will try to delete the same pointer value twice.