Aaron T
Aaron T

Reputation: 1102

Visual Studio C++ "ProjectName.exe has triggered a breakpoint" and other issues

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

Answers (1)

Miles Budnek
Miles Budnek

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 news that same memory, and then your heap gets screwed up.

Upvotes: 5

Related Questions