Reputation: 1102
I've looked into this issue, but I haven't really received a good answer so I thought I'd ask here.
I'm working on project in C++ that involves a bit of memory manipulation (allocating dynamic char arrays and putting memory addresses and full objects inside those arrays using placement new).
I'm running into a very frustrating problem every time I debug the program in Visual Studio: a message pops up usually saying "ProjectName.exe has triggered a breakpoint." and sometimes saying something like "Exception thrown at 0x0FFD66CB (ucrtbased.dll) in 2 - Kennel.exe: 0xC0000005: Access violation reading location 0xFFFFFFF5."
Here's the code for the relevant classes
Cat.h
#ifndef CAT_H
#define CAT_H
#include <string>
#include "Kennel.h"
using std::string;
class Cat {
private:
static Kennel catKennel;
int _id;
string _name;
Cat(int i, const string& nm) : _name(nm) { // Note initializer list
_id = i;
}
void* operator new(size_t size){
return catKennel.allocate();
}
public:
static Cat* create(int id, const string& name) { // Factory method
return new Cat(id, name);
}
void operator delete(void* loc) {
catKennel.deallocate(loc);
}
};
Kennel Cat::catKennel(sizeof(Cat), 5);
#endif
Kennel.h
#ifndef KENNEL_H
#define KENNEL_H
#include <cstddef>
#include <cassert>
#include <iostream>
#include <new>
class Kennel {
private:
static const size_t _BUCKET_COUNT = 10;
const size_t _BUCKET_SIZE;
const size_t _ELEM_SIZE;
char* buckets[_BUCKET_COUNT];
//char** buckets;
char* availableBlock;
public:
Kennel(size_t elemSize, size_t bucketSize = 5);
~Kennel();
void* allocate(); // Get a pointer inside a pre-allocated block for a new object
void deallocate(void*); // Free an object's slot (push the address on the "free list")
};
#endif
Kennel.cpp
#include "Kennel.h"
Kennel::Kennel(size_t elemSize, size_t bucketSize) : _ELEM_SIZE(elemSize), _BUCKET_SIZE(bucketSize) {
//Set each element in buckets to nullptr.
for (int i = 0; i < _BUCKET_COUNT; i++)
buckets[i] = nullptr;
}
Kennel::~Kennel() {
//Delete each character array in buckets.
for (int i = 0; i < _BUCKET_COUNT; i++) {
if (buckets[i] != nullptr)
delete[] buckets[i];
}
}
void* Kennel::allocate() {
//If there is no available bucket: create a new one.
if (availableBlock == nullptr) {
//Find next array index in buckets array where we can create a new bucket.
int nextNullBucketIndex = -1;
for (int i = 0; i < _BUCKET_COUNT; i++) {
if (buckets[i] == nullptr) {
nextNullBucketIndex = i;
break;
}
}
assert(nextNullBucketIndex > -1); //If there is no space to create another bucket: exit.
//Create a new bucket.
buckets[nextNullBucketIndex] = new char(_BUCKET_SIZE * _ELEM_SIZE);
availableBlock = buckets[nextNullBucketIndex];
char* tempBlock = availableBlock;
//Put the address of the next block inside each block.
std::cout << "Bucket " << nextNullBucketIndex << ": ";
for (int i = 0; i < _BUCKET_SIZE - 1; i++) {
std::cout << static_cast<void*>(tempBlock) << ' ';
new (tempBlock) char*(tempBlock += _ELEM_SIZE);
}
std::cout << static_cast<void*>(tempBlock) << std::endl;
new (tempBlock) char*(nullptr); //The last block doesn't get an address, put nullptr in it.
}
char* tempAvailable = availableBlock;
availableBlock = *reinterpret_cast<char**>(availableBlock);
std::cout << "Returning: " << static_cast<void*>(tempAvailable) << std::endl;
return static_cast<void*>(tempAvailable);
}
void Kennel::deallocate(void* loc) {
new (loc) char*(availableBlock); //Store availableBlock's contained address in loc.
availableBlock = static_cast<char*>(loc); //Set availableBlock's contained address to be loc's address.
}
The problem differs each time I run my program. Here is a bit of information about the more common problems that I get when trying to run it.
It often breaks when trying to execute Kennel.cpp line 48, in allocate
std::cout << "Returning: " << static_cast<void*>(tempAvailable) << std::endl;
It often breaks on a line in my main function that deletes Cat objects.
It sometimes breaks at the end of the program when it's calling Kennel's deconstructor.
Etc...
As I said, it is seemingly random which line of code it will decide to break on. Also, I am almost sure that the problem is with the Kennel class, because I used my code using my friend's Kennel.h and Kennel.cpp and it worked flawlessly.
Any help would be extremely appreciated.
Upvotes: 4
Views: 18861
Reputation: 30639
buckets[nextNullBucketIndex] = new char(_BUCKET_SIZE * _ELEM_SIZE);
This allocates only a single char
, not an array of them. You then proceed to write stuff into memory you don't own. std::string
also new
s that same memory, and then your heap gets screwed up.
Upvotes: 5