After I run this program, and input the Value: value in main(), I get the error "HEAP CORRUPTION DETECTED":
I've spent about an hour trying to figure out what's causing this, would appreciate help in what caused this and what exactly I need to do to fix this.
Thanks!
#include <iostream>
using namespace std;
template <class t>
class dynamicDataHolder
{
t* ptr = NULL;
int size = 0;
int elements = 0;
public:
dynamicDataHolder() {
ptr = new t[5];
size = 5;
elements = 0;
}
dynamicDataHolder(int arraySize) {
ptr = new t[size];
size = arraySize;
elements = 0;
}
dynamicDataHolder(const dynamicDataHolder<t>& obj) {
ptr = new t[obj.size];
if (ptr != NULL) {
for (int i = 0; i < obj.size; i++) {
ptr[i] = obj.ptr[i];
}
}
else {
cout << "\nPointer to the holder was found to be NULL." << endl;
}
elements = obj.elements;
size = obj.size;
}
t getData(int pos) {
if (pos < 0 || pos > size)
{
cout << "\nError: Invalid index!" << endl;
return 0;
}
return ptr[pos];
}
int sizeA() {
return size;
}
int capacity() {
int capacity = size - elements;
return capacity;
}
void insert(t data) {
int pos;
if (elements >= size) {
int updatedSize = (2 * size);
t* tempPtr = ptr;
ptr = new t[updatedSize];
for (int i = 0; i < size; ++i) {
ptr[i] = tempPtr[i];
}
delete[] tempPtr;
tempPtr = NULL;
size = updatedSize;
pos = elements;
ptr[pos] = data;
incrementElement();
}
else {
if (elements == 0) {
pos = 0;
ptr[pos] = data;
incrementElement();
}
else {
pos = elements;
ptr[pos] = data;
incrementElement();
}
}
}
bool compare(const dynamicDataHolder<t>& obj)
{
bool result = false;
if (obj.size != size || obj.elements != elements) {
return result;
}
for (int i = 0; i < size; i++) {
result = false;
if (ptr[i] == obj.ptr[i])
{
result = true;
}
}
return result;
}
void resize(int updatedSize) {
t* tempPtr = ptr;
ptr = new t[updatedSize];
if (updatedSize < size) {
cout << "\nWarning! Entered size is less than the current size." << endl
<< "Loss of data may occur. Continue? (Y/N)" << endl;
Select:
char userChoice; cin >> userChoice;
if (userChoice == 'y' || userChoice == 'Y') {
goto Continue;
}
else if (userChoice == 'n' || userChoice == 'N') {
return;
}
else {
cout << "\nInvalid selection - please try again." << endl;
goto Select;
}
}
Continue:
if (ptr != NULL) {
for (int i = 0; i < size; i++) {
ptr[i] = tempPtr[i];
}
delete[] tempPtr;
tempPtr = NULL;
size = updatedSize;
}
else {
cout << "\nPointer to the holder was found to be NULL." << endl;
}
}
void insertAtNthPos()
{
cout << "\nEnter the details below to insert values:-\n" << endl;
cout << "\nPosition: ";
int pos = 0; cin >> pos;
cout << "Value: ";
t value; cin >> value;
if (pos <= size)
{
ptr[pos] = value;
if (pos > elements) {
incrementElement();
}
}
else {
resize(2 * size);
ptr[pos] = value;
incrementElement();
}
}
dynamicDataHolder& operator=(const dynamicDataHolder& rhs) {
delete[] ptr;
ptr = new t[rhs.size];
if (this != &rhs)
{
for (int i = 0; i < rhs.size; i++)
{
ptr[i] = rhs.ptr[i];
}
}
return *this;
}
~dynamicDataHolder()
{
delete[] ptr;
ptr = NULL;
}
void incrementElement()
{
elements += 1;
}
};
int main()
{
cout << "What do you want to do?" << endl
<< "\t1. Run Test Sequence" << endl
<< "\t0. Exit Program" << endl;
Select:
int userChoice = 0; cin >> userChoice;
if (userChoice == 1) {
goto testSequence;
}
else if (userChoice == 0) {
return 0;
}
else {
cout << "\nInvalid selection - please try again." << endl;
goto Select;
}
testSequence:
system("CLS");
dynamicDataHolder<int> obj;
dynamicDataHolder<char> obj2(5);
dynamicDataHolder<char> obj3 = obj2;
bool result = (obj3.compare(obj2));
if (result == 1) {
cout << "True" << endl;
}
else {
cout << "\nFalse" << endl;
}
for (int i = 0; i < 5; i++) {
obj.insert(i * 2);
}
cout << "Capacity: " << obj.capacity() << endl;
cout << "Size: " << obj.sizeA() << endl;
obj.resize(15);
cout << obj2.getData(4) << endl;
obj2.insertAtNthPos();
dynamicDataHolder<int> obj4;
obj4 = obj;
}
Free code review.
Here's a simplified version of your code
replacing goto with loops (this revolution in language development happened in 1970, get with the times!)
use the right names! Naming is important.
In particular
capacity means "limit to potential size" (what you names "size" or "arraySize" before)
size means "count of elements in the collection" (e.g. size grows on insertion)
(capacity - size) is free space/room/available capacity. This is what you named "capacity" before
Violating expectations of identifier meanings leads to bugs. In fact, you had that, because your own test sequence used getData(4) on a holder that contained 0 elements (_size == 0).
Same for operations. insert(...) should insert, not overwrite (see below), resize(...) should change the size, not the capacity.
To change the capacity, the standard library uses the term reserve(...), which I also used for consistency and least-surprise.
Also, names should be descriptive. In a strong typed language naming the variable after its type is redundant information. Also, you can names variables strA, strB, strC but it's more informative to name them givenName, middleNames, surName. Note that suddenly we have semantic information (is it lastName, surName, birthName, familyName. So much information is conveyed).
In this sense, I renamed ptr to _data
While we're at it, we might rename the class to be descriptive: DynaArray<T> tells you more than dynamicDataHolder<T> (that could be std::optional<T>, std::unique_ptr<T>, std::vector<T>, harddiskVolume etc).
As a minor footnote it is a good idea to have a naming convention for (private) member variables, so you will know what variables are local and which are member. I used _data for _ptr
You had a number of places (2 if I remember correctly) where you had off-by-one boundary checks (think pos > capacity instead of >=).
Separation Of Concerns. dynamicDataHolder<T> is a data holder, not an interactiveUserInterviewAboutErrorConditions<T> or databaseUpdateUserInterface<T>. So, don't do UI in the class. Change void insertAtNthPos() to void setData(size_t pos, T value).
Again, naming is important. Don't promise to insert if really you don't unless it's beyond current size bounds. "Insert 7 at pos 1 in {1,2,3}" should result in "{1,7,2,3}", not "{1,7,3}". The name setData
does what is expected and
separates UI concerns and
nicely mirrors getData(size_t pos)
Oh, use consistent index types (size_t in my version) so that you avoid funny looking checks (pos > 0?) and bugs due to mixed signed/unsigned comparisons.
Yes, you had them. No you wouldn't have caught them because you didn't have compiler warnings enabled. Let this reinforce the mantra: "Use The Force Diagnostics"
Don't do "using namespace" at global scope. Or not at all (Why is "using namespace std;" considered bad practice?)
DRY. DRY. DRY. (Don't Repeat Yourself). Sooo many examples of this. You had at least 3 copy-element-data loops. You had several places that resize the allocated array. You had funny stuff like:
void insert(t data) {
int pos;
if (elements >= size) {
int updatedSize = (2 * size);
t* tempPtr = ptr;
ptr = new t[updatedSize];
for (int i = 0; i < size; ++i) {
ptr[i] = tempPtr[i];
}
delete[] tempPtr;
tempPtr = NULL;
size = updatedSize;
pos = elements;
ptr[pos] = data;
incrementElement();
}
else {
if (elements == 0) {
pos = 0;
ptr[pos] = data;
incrementElement();
}
else {
pos = elements;
ptr[pos] = data;
incrementElement();
}
}
It should be obvious that this does the exact same thing three times. The if (elements == 0) check is completely useless, because if you don't have the check, the else branch does the same anyways. For comparison, the new insert looks like:
void insert(T data) { setData(_size, data); }
Oh oops, that got completely zapped, because really, there is nothing special about inserting. But setData is also only ~4 lines of code.
when you add items you have to be careful: you had a very helpful "capacity check". And if the capacity was not enough you doubled capacity. HOWEVER. Who said that was enough? You need to either keep doubling until space is adequate:
void setData(size_t pos, T value) {
while (pos >= _capacity) {
grow();
}
_size = std::max(_size, pos + 1);
_data[pos] = value;
}
or reserve sufficient size at once:
size_t required = _capacity;
while (pos >= required)
required *= 2;
reserve(required);
Const-Correctness: all the observers should be marked const so the compiler knows what it can optimize and the user has guarantees what will never change:
size_t size() const { return _size; }
size_t capacity() const { return _capacity; }
size_t available() const { return _capacity - _size; }
bool compare(const DynArray<T>& obj) const;
The repeated copy/initialization loops can be fixed by using the copy constructor. In related context, make the constructor use a base/member initializer list:
explicit DynArray(size_t initialCapacity = 5)
: _capacity(initialCapacity),
_data(initialCapacity != 0u ? new T[_capacity]{} : nullptr) {}
Now the copy constructor can be:
DynArray(DynArray const& obj) : DynArray(obj.capacity()) {
_size = obj.size();
for (size_t i = 0; i < capacity(); ++i)
_data[i] = obj._data[i];
}
That's the last time you will ever see a copy loop. Why? Observe:
How would you reserve more / less space and without manually copying items? Wouldn't you need a dynamic array class of some kind?
Oh wait. You're implementing it! Don't forget the code you already have:
void reserve(size_t updatedSize) {
DynArray tmp(updatedSize);
for (size_t i = 0; i < std::min(capacity(), tmp.capacity()); ++i) {
tmp._data[i] = _data[i];
}
*this = std::move(tmp);
}
void grow() { reserve(2 * _capacity); }
Now the real magic is in the move constructor/assignment operations.
Copy-and-Swap idiom to the rescue. Turns out you can implement these operations really efficiently and generally with little code:
DynArray(DynArray&& obj) : DynArray(0) { swap(obj); }
DynArray& operator=(DynArray rhs) {
swap(rhs);
return *this;
}
See. Simplicity is a driver. The key is that you just swap with a temporary. The temporary gets destructed, as you want. The swap is pretty simple in its own right:
void swap(DynArray& rhs) {
assert(rhs._data);
std::swap(_capacity, rhs._capacity);
std::swap(_size, rhs._size);
std::swap(_data, rhs._data);
}
There's a lot more thinking behind Copy-And-Swap, but you can find that on your own if you really want: What is the copy-and-swap idiom?
compare checked capacities. I'd say that from a logical standpoint that shouldn't matter.
DynArray<int> a(20), b(12);
for (int v : {1,2,3}) { a.insert(v); b.insert(v); }
assert(a == b); // should pass
it also had bugs, a spurious
result = false;
and erroneous
if (ptr[i] == obj.ptr[i])
{
result = true;
}
That made no sense. If a single item matches, doesn't mean all the previous mismatches are gone.
The above has removed a lot of code, and many many lurking bugs or maintenance head-aches. I reworded your test code to be ... readable. The gotos are obviously gone, the main is now "sane":
int main() {
std::cout << "What do you want to do?\n"
<< "\t1. Run Test Sequence\n"
<< "\t0. Exit Program\n";
for (int userChoice = 0; std::cin >> userChoice;) {
switch (userChoice) {
case 1: testSequence(); break;
case 0: return 0;
default: std::cout << "\nInvalid selection - please try again.\n";
}
}
}
And the information displayed is more complete and much less error prone. Naming can still be improved (obj2? obj4?!??) but I had no semantic information to go by, so I left that as an exorcism for the reader, and also so that you will have an easy time cross-referencing your own test sequence code with mine.
Live Demo
Live On Coliru
#include <iostream>
#include <stdexcept>
#include <cassert>
template <class T> class DynArray {
size_t _capacity = 0;
size_t _size = 0;
T* _data = nullptr;
public:
explicit DynArray(size_t initialCapacity = 5)
: _capacity(initialCapacity),
_data(initialCapacity != 0u ? new T[_capacity]{} : nullptr) {}
DynArray(DynArray const& obj) : DynArray(obj.capacity()) {
_size = obj.size();
for (size_t i = 0; i < capacity(); ++i)
_data[i] = obj._data[i];
}
~DynArray() { delete[] _data; }
void swap(DynArray& rhs) {
assert(rhs._data);
std::swap(_capacity, rhs._capacity);
std::swap(_size, rhs._size);
std::swap(_data, rhs._data);
}
DynArray(DynArray&& obj) : DynArray(0) { swap(obj); }
DynArray& operator=(DynArray rhs) {
swap(rhs);
return *this;
}
T getData(size_t pos) const {
if (pos >= _size) {
throw std::out_of_range("pos");
}
return _data[pos];
}
void setData(size_t pos, T value) {
while (pos >= _capacity)
grow();
_size = std::max(_size, pos+1);
_data[pos] = value;
}
void insert(T data) { setData(_size, data); }
size_t size() const { return _size; }
size_t capacity() const { return _capacity; }
size_t available() const { return _capacity - _size; }
bool compare(const DynArray& obj) const {
if (obj._size != _size) {
return false;
}
for (size_t i = 0; i < _size; i++) {
if (_data[i] != obj._data[i])
return false;
}
return true;
}
void reserve(size_t updatedSize) {
DynArray tmp(updatedSize);
for (size_t i = 0; i < std::min(capacity(), tmp.capacity()); ++i) {
tmp._data[i] = _data[i];
}
*this = std::move(tmp);
}
void grow() { reserve(2 * _capacity); }
};
template <typename T>
void dump(std::string caption, DynArray<T> const& da) {
std::cout << caption << " Capacity: " << da.capacity() << ","
<< " Size: " << da.size() << ","
<< " Available: " << da.available() << ","
<< " Data: {";
for (size_t i = 0; i < da.size(); i++)
std::cout << " " << static_cast<int>(da.getData(i));
std::cout << " }\n";
}
void testSequence() {
{
DynArray<int> obj;
for (size_t i = 0; i < 5; i++) {
obj.insert(i * 2);
}
dump("obj", obj);
if (15 < obj.capacity()) {
std::cout << "\nWarning! Entered size is less than the current size.\n"
<< "Loss of data may occur. Continue? (Y/N)\n";
for (char userChoice; std::cin >> userChoice;) {
if (userChoice == 'y' || userChoice == 'Y') {
obj.reserve(15);
break;
}
if (userChoice == 'n' || userChoice == 'N') {
break;
}
std::cout << "\nInvalid selection - please try again.\n";
}
}
DynArray<int> obj4;
obj4 = obj;
dump("obj4", obj);
}
DynArray<char> obj2(5);
DynArray<char> obj3 = obj2;
dump("obj2", obj2);
dump("obj3", obj3);
std::cout << "obj2 == obj3: " << std::boolalpha << obj3.compare(obj2) << "\n";
try {
std::cout << "obj2.getData(4): " << obj2.getData(4) << std::endl;
} catch(std::out_of_range const& e) {
std::cout << "out of range: " << e.what() << std::endl;
}
while (true) {
std::cout << "\nEnter the details below to insert obj2 values:-\n";
std::cout << "\nPosition: ";
long pos = 0;
if (std::cin >> pos) {
if (pos < 0)
break;
std::cout << "Value: ";
int value = 0;
if (std::cin >> value) {
obj2.setData(pos, value);
}
}
if (!std::cin.eof())
std::cin.clear();
else
break;
dump("obj2", obj2);
}
}
int main() {
do {
std::cout << "What do you want to do?\n"
<< "\t1. Run Test Sequence\n"
<< "\t0. Exit Program\n";
if (int userChoice = 0; std::cin >> userChoice) {
switch (userChoice) {
case 1: testSequence(); continue;
case 0: std::cout << "Goodbye\n"; return 0;
default: std::cout << "\nInvalid selection - please try again.\n";
}
}
if (!std::cin.eof())
std::cin.clear();
} while (std::cin.good());
}
When run with e.g.
g++ -std=c++17 -O2 -Wall -Wextra -pedantic -pthread -fsanitize=address,undefined main.cpp -o sotest
./sotest <<< "1 30 99 3 42 0 -1 0 8 -1 1 -1 0"
You will see the following output:
What do you want to do?
1. Run Test Sequence
0. Exit Program
obj Capacity: 5, Size: 5, Available: 0, Data: { 0 2 4 6 8 }
obj4 Capacity: 5, Size: 5, Available: 0, Data: { 0 2 4 6 8 }
obj2 Capacity: 5, Size: 0, Available: 5, Data: { }
obj3 Capacity: 5,
Size: 0, Available: 5, Data: { } obj2 == obj3: true
obj2.getData(4): out of range: pos
Enter the details below to insert obj2 values:-
Position: Value: obj2 Capacity: 40, Size: 31, Available: 9, Data: { 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 99 }
Enter the details below to insert obj2 values:-
Position: Value: obj2 Capacity: 40, Size: 31, Available: 9, Data: { 0
0 0 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 99 }
Enter the details below to insert obj2 values:-
Position: Value: obj2 Capacity: 40, Size: 31, Available: 9, Data: { -1
0 0 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 99 }
Enter the details below to insert obj2 values:-
Position: Value: obj2 Capacity: 40, Size: 31, Available: 9, Data: { 8
0 0 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 99 }
Enter the details below to insert obj2 values:-
Position: What do you want to do?
1. Run Test Sequence
0. Exit Program
obj Capacity: 5, Size: 5, Available: 0, Data: { 0 2 4 6 8 }
obj4 Capacity: 5, Size: 5, Available: 0, Data: { 0 2 4 6 8 }
obj2 Capacity: 5, Size: 0, Available: 5, Data: { }
obj3 Capacity: 5,
Size: 0, Available: 5, Data: { }
obj2 == obj3: true
obj2.getData(4): out of range: pos
Enter the details below to insert obj2 values:-
Position: What do you want to do?
1. Run Test Sequence
0. Exit Program
Goodbye
Related
So I'm trying to create this priority queue to handle my "Order" objects, I'm running into a problem where an object containing the same key/priority will be placed at an early earlier position than others initialized first. I have provided the expected and received output alongside the 83 lines of code of how I constructed my heap with notes
#include <iostream>
#include <vector>
struct Order {
int value = -1;
int priority = -1;
bool operator <(Order const& RHS) { return priority < RHS.priority; }
};
class heap {
private:
std::vector<Order> orders{ Order{} };
int size{}; //initalizes it at 0
int p(int index) { return index >> 1; }
int l(int index) { return index << 1; }
int r(int index) { return (index << 1) + 1; }
public:
bool isEmpty() const { return size == 0; }
void shiftUp(int position);
void shiftDown(int position);
void add(Order new_entry);
Order removeTop();
Order& getTop() { return orders[1]; }
};
template <typename T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
heap h;
h.add(Order{1,3}); h.add(Order{2,2});
h.add(Order{3,3}); h.add(Order{5,1});
h.add(Order{6,2}); h.add(Order{7,2});
h.add(Order{8,3}); h.add(Order{9,1});
h.add(Order{23,3});
std::cout << "value" << " key(priority)" << "\n";
for (int i = 0; i < 8; i++) {
Order temp = h.removeTop();
std::cout << temp.value << "\t " << temp.priority << "\n";
}
}
void heap::shiftUp(int position) {
if (position > size) return;
if (position == 1) return;
if (orders[p(position)] < orders[position]) {
mySwap(orders[position], orders[p(position)]);
shiftUp(p(position));
}
}
void heap::shiftDown(int position) {
if (position > size) return;
int greaterPosition = position;
if (l(position) <= size && orders[position] < orders[l(position)])
greaterPosition = l(position);
if (r(position) <= size && orders[greaterPosition] < orders[r(position)])
greaterPosition = r(position);
if (greaterPosition != position) {
mySwap(orders[position], orders[greaterPosition]);
shiftDown(greaterPosition);
}
}
void heap::add(Order new_entry) {
if (size + 1 >= orders.size()) orders.push_back(Order{});
orders[++size] = new_entry;
shiftUp(size);
}
Order heap::removeTop() {
Order temp = orders[1];
mySwap(orders[1],orders[orders.size() - 1]); size--;
orders.pop_back();
shiftDown(1);
return temp;
}
/*
Expected Output
Value key(priority)
1 3
3 3
8 3
23 3
2 2
6 2
7 2
5 1
9 1
Recieved/wrong Output
value key(priority)
1 3
23 3
3 3
8 3
2 2
6 2
7 2
5 1
*/
Fixed code from answered information above
#include <iostream>
#include <vector>
struct Order {
int value = -1;
int priority = -1;
int FIFO;
bool operator <(Order const& RHS) {
if (priority == RHS.priority)
return FIFO > RHS.FIFO;
else
return priority < RHS.priority;
} //compares keys for larger presidence
};
class heap {
private:
std::vector<Order> orders{ Order{} };
int size{}; //initalizes it at 0
int p(int index) { return index >> 1; }
int l(int index) { return index << 1; }
int r(int index) { return (index << 1) + 1; }
public:
bool isEmpty() const { return size == 0; }
void shiftUp(int position);
void shiftDown(int position);
void add(Order new_entry);
Order removeTop();
Order& getTop() { return orders[1]; }
};
template <typename T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
heap h;
h.add(Order{1,3}); h.add(Order{2,2});
h.add(Order{3,3}); h.add(Order{5,1});
h.add(Order{6,2}); h.add(Order{7,2});
h.add(Order{8,3}); h.add(Order{9,1});
h.add(Order{23,3});
std::cout << "value" << " key(priority)" << "\n";
for (int i = 0; i < 8; i++) {
Order temp = h.removeTop();
std::cout << temp.value << "\t " << temp.priority << "\n";
}
}
void heap::shiftUp(int position) {
if (position > size) return;
if (position == 1) return;
if (orders[p(position)] < orders[position]) {
mySwap(orders[position], orders[p(position)]);
shiftUp(p(position));
}
}
void heap::shiftDown(int position) {
if (position > size) return;
int greaterPosition = position;
if (l(position) <= size && orders[position] < orders[l(position)])
greaterPosition = l(position);
if (r(position) <= size && orders[greaterPosition] < orders[r(position)])
greaterPosition = r(position);
if (greaterPosition != position) {
mySwap(orders[position], orders[greaterPosition]);
shiftDown(greaterPosition);
}
}
void heap::add(Order new_entry) {
if (size + 1 >= orders.size()) orders.push_back(Order{});
new_entry.FIFO = size + 1;
orders[++size] = new_entry;
shiftUp(size);
}
Order heap::removeTop() {
Order temp = orders[1];
mySwap(orders[1],orders[orders.size() - 1]); size--;
orders.pop_back();
shiftDown(1);
return temp;
}
In general, heap does not have FIFO property until you implement something that helps doing so. In your order class, you are only comparing using the priority value. In your Order class, you are comparing two Orders by only their priority value. You need a additional variable that serves as the purpose for recording the timing when that value was inserted, and compare according to that.
If you are using the variable value for that purpose, you need to specify in your overloaded < method, what do you want to do when two Order's priority values are equal. Currently, you are only using the priority variable to compare. You are not specifying what do you want to do when the priority of two Orders are equal. You have to specify what do you want to do when the priority value of two variables are equal. Maybe compare a timing variable.
I'm trying to implement a MinHeap, where objects on the heap are WorkerNodes. My method returns map which is intended to allow client code to determine which WorkerNode indices have changed from the minHeapify operation.
std::cout << "heapifying " << heap_[root] << "from index " << root << "\n.";
int size = heap_.size();
bool swapped = false;
std::map<WorkerNode, int> tracker;
for (int i = root; i >= 0; --i)
{
while (true)
{
int leftChild = 2 * i + 1;
if (leftChild < 0 || leftChild >= size)
break;
int rightChild = 2 * i + 2;
int smallerChild = leftChild;
if (rightChild < size && heap_[rightChild] < heap_[leftChild])
smallerChild = rightChild;
if (heap_[i] <= heap_[smallerChild])
break;
// index tracking
tracker[heap_[i]] = smallerChild;
tracker[heap_[smallerChild]] = i;
std::cout << "**\n\n"
<< heap_[i] << " has moved to " << smallerChild;
std::cout << ", and " << heap_[smallerChild] << " has moved to " << i << "\n**";
// swap heap_[i] and heap_[smallerChild]
swapped = true;
T temp = heap_[i];
heap_[i] = heap_[smallerChild];
heap_[smallerChild] = temp;
i = smallerChild;
}
}
if (!swapped) // avoids bad access
{
tracker[heap_[root]] = root;
for (auto &itm : tracker)
{
std::cout << "**\n"
<< itm.first << " is at " << itm.second << "!!!\n";
}
std::cout << "**\nno swap; " << heap_[root] << " stays at " << tracker[heap_[root]] << "\n**";
}
return tracker;
Here is the ouput that I am seeing:
heapifying W1-1from index 0
.**
W1-1 is at 0!!!
**
no swap; W1-1 stays at 0
**heapifying W2-2from index 1
.**
W2-2 is at 1!!!
**
no swap; W2-2 stays at 0
**heapifying W3-3from index 2
.**
W3-3 is at 2!!!
**
no swap; W3-3 stays at 0
**heapifying W0-3from index 3
.**
W0-3 is at 3!!!
**
no swap; W0-3 stays at 0
This issue was brought to my attention when running test cases, where I am doing something like this:
WorkerNode key("W4", 2);
// after two decrements, its index should still be 3.
BOOST_TEST(tracker[key] == 3);
And getting output like this:
error: in "minheap_test_suite/case6": check tracker[key] == 3 has failed [0 != 3]
So from what I can tell, The pre-exit for loop in my minHeapify method confirms that the proper data is being inserted into the map, but when I try to access this data using the [] operator, it is unable to locate the WorkerNode-index pairing I just inserted, returning 0 as the value it has probably just default-constructed.
When I tried using find() instead of [] just now like so:
tracker[heap_[root]] = root;
for (auto &itm : tracker)
{
std::cout << "**\n"
<< itm.first << " is at " << itm.second << "!!!\n";
}
int index = tracker.find(heap_[root])->second;
std::cout << "**\nno swap; " << heap_[root] << " stays at " << index << "\n**";
I get the following output:
heapifying W1-1from index 0
.**
W1-1 is at 0!!!
**
no swap; W1-1 stays at -1354735968
**heapifying W2-2from index 1
.**
W2-2 is at 1!!!
**
no swap; W2-2 stays at 3233540
Here is my WorkerNode.h file, comments removed:
#include <ostream>
#include <string>
struct WorkerNode
{
unsigned numJobs_; ///< worker job count.
std::string workerID_; ///< worker ID string.
explicit WorkerNode() : numJobs_(0), workerID_("") {}
WorkerNode(std::string id) : numJobs_(0), workerID_(id) {}
WorkerNode(std::string id, unsigned jobs) : numJobs_(jobs), workerID_(id) {}
WorkerNode(WorkerNode &&other) : numJobs_(other.numJobs_), workerID_(other.workerID_)
{
other.numJobs_ = 0;
other.workerID_ = "";
}
WorkerNode(const WorkerNode &other) : numJobs_(other.numJobs_), workerID_(other.workerID_) {}
WorkerNode &operator=(const WorkerNode &other)
{
if (this == &other)
return *this;
this->numJobs_ = other.numJobs_;
this->workerID_ = other.workerID_;
return *this;
}
WorkerNode &operator=(WorkerNode &&other)
{
if (this == &other)
return *this;
this->numJobs_ = other.numJobs_;
this->workerID_ = other.workerID_;
other.numJobs_ = 0;
other.workerID_ = "";
return *this;
}
~WorkerNode() {}
bool operator<(const WorkerNode &rhs) const
{
return *this <= rhs;
}
bool operator<=(const WorkerNode &rhs) const
{
if (numJobs_ < rhs.numJobs_)
return true;
else if (rhs.numJobs_ < numJobs_)
return false;
else
{
return workerID_.compare(rhs.workerID_) <= 0 ? true : false;
}
}
bool operator==(const WorkerNode &rhs) const
{
if (numJobs_ == rhs.numJobs_ && workerID_ == rhs.workerID_)
return true;
else
{
return false;
}
}
void operator--()
{
if (numJobs_ > 0)
numJobs_ -= 1;
}
void operator++()
{
numJobs_ += 1;
}
friend std::ostream &operator<<(std::ostream &out, const WorkerNode &n)
{
out << n.workerID_ << "-" << n.numJobs_;
return out;
}
};
WTF am I doing wrong here?
EDIT:
Okay folks, here is my reprex. My apologies for the prior nonsensical code bloat. This example 100% reproduces my current confusion. Let's get to the bottom of this thing.
Key.h:
// user-defined struct, intended to be used as a map key.
#include <string>
#include <ostream>
struct Key
{
std::string id;
unsigned jobs;
Key(std::string id_ = "", unsigned jobs_ = 0) : id(id_), jobs(jobs_) {}
bool operator<(const Key &rhs) const
{
if (jobs < rhs.jobs)
return true;
else if (rhs.jobs < jobs)
return false;
else
return id.compare(rhs.id) <= 0 ? true : false;
}
bool operator<=(const Key &rhs) const
{
if (jobs < rhs.jobs)
return true;
else if (rhs.jobs < jobs)
return false;
else
return id.compare(rhs.id) <= 0 ? true : false;
}
friend std::ostream &operator<<(std::ostream &o, const Key &key)
{
o << key.id << "-" << key.jobs;
return o;
}
};
MinHeap.h:
#include <vector>
#include <map>
#include "Key.h"
struct MinHeap
{
std::vector<Key> heap;
std::map<Key, int> minHeapify(int root)
{
std::map<Key, int> tracker;
for (int i = 0; i < heap.size(); ++i)
tracker[heap[i]] = i;
return tracker;
}
std::map<Key, int> insert(const Key &key)
{
heap.push_back(key);
return minHeapify(heap.size() - 1);
}
};
main.cpp:
#include <iostream>
#include "MinHeap.h"
int main()
{
MinHeap heap;
std::map<Key, int> tracker;
for (int i = 0; i < 3; ++i)
{
Key key("Key" + std::to_string(i), i);
tracker = heap.insert(key);
//checking tracker contents using auto for loop
std::cout << "tracker keyindex contents: ";
for (auto &itm : tracker)
{
std::cout << itm.first << " ::: " << itm.second << ", ";
}
std::cout << "\n\n";
//checking key and tracker[key], which should reflect
//each other as well as the operation done in minHeapify.
/// *** what tracker[key] is actually printing ***
std::cout << "tracker[key] = " << tracker[key] << std::endl;
/// **********************************************
/// *** what tracker[key] is expected to be printing ***
std::cout << "actual tracker key index: " << key.jobs << std::endl;
/// ****************************************************
}
return 0;
}
Run main.cpp yourself. The big problem here is the last two print statements. The prior for loop confirms that the expected Keys are indeed being returned by the minHeapify(int) operation and have the expected index.
However, attempts to subindex using [Key] into map<Key,int> does not return the expected index.
Hopefully, I have illustrated the confusion a little clearer.
Thanks in advance for the help.
Cheers
The problem, I think, is already identified in comments by Remy Lebeau.
Your implementation of Key::operator< does not meet the requirements of strictly weak ordering, as required of a type to be usable as the key in a std::map.
You need a minor change in the implementation.
bool operator<(const Key &rhs) const
{
if (jobs < rhs.jobs)
return true;
else if (rhs.jobs < jobs)
return false;
else
return id.compare(rhs.id) < 0 ? true : false; // Needs to be <, not <=
}
You can simplify the function using std::tie
bool operator<(const Key &rhs) const
{
std::tie(jobs, id) < std::tie(rhs.jobs, rhs.id);
}
This is what a minimal reproducible example looks like:
#include <iostream>
#include <map>
struct Key {
std::string id;
unsigned jobs;
bool operator<(const Key & rhs) const {
if (jobs < rhs.jobs)
return true;
else if (rhs.jobs < jobs)
return false;
else
return id.compare(rhs.id) <= 0 ? true : false;
}
};
int main() {
std::map<Key, int> m;
m[Key { "Key0", 0 }] = 0;
m[Key { "Key1", 1 }] = 1;
m[Key { "Key2", 2 }] = 2;
std::cout << m[Key { "Key0", 0 }] << std::endl;
std::cout << m[Key { "Key1", 1 }] << std::endl;
std::cout << m[Key { "Key2", 2 }] << std::endl;
return 0;
}
Is this easier to grasp? Is this easier for people to help you with?
Can you yourself find the problem now?
I am trying to implement a Dynamic Stack in c++.
i have 3 members in class stack
1.cap is the capacity.
2.top- points to top of stack
3. arr- pointer to an integer.
in the class constrcutor I am allocating memory to stack(malloc).
later in the meminc() I am trying to realloc the memory.
I have written a function meminc() to realloc the memory but i get this invalid old size error.
It would be helpful if you let me know what is wrong in this code. I will also appreciate any advice given to me.
Thank you.
#include <iostream>
using namespace std;
#define MAXSIZE 5
class stack {
int cap;
int top;
int *arr;
public:
stack();
bool push(int x);
bool full();
bool pop();
bool empty();
bool meminc();
};
stack::stack()
{
cap = MAXSIZE;
arr = (int *)malloc(sizeof(int)*MAXSIZE);
top = -1;
}
bool stack::meminc()
{
cap = 2 * cap;
cout << cap << endl;
this->arr = (int *)realloc(arr, sizeof(int)*cap);
return(arr ? true : false);
}
bool stack::push(int x)
{
if (full())
{
bool x = meminc();
if (x)
cout << "Memory increased" << endl;
else
return false;
}
arr[top++] = x;
return true;
}
bool stack::full()
{
return(top == MAXSIZE - 1 ? true : false);
}
bool stack::pop()
{
if (empty())
return false;
else
{
top--;
return true;
}
}
bool stack::empty()
{
return(top == -1 ? true : false);
}
int main()
{
stack s;
char y = 'y';
int choice, x;
bool check;
while (y == 'y' || y == 'Y')
{
cout << " 1.push\n 2.pop\n" << endl;
cin >> choice;
switch (choice)
{
case 1: cout << "Enter data?" << endl;
cin >> x;
check = s.push(x);
cout << (check ? " push complete\n" : " push failed\n");
break;
case 2: check = s.pop();
cout << (check ? " pop complete\n" : " pop failed\n");
break;
default: cout << "ERROR";
}
}
}
To add to john's answer,
the way you're using realloc() is ... flawed.
bool stack::meminc()
{
cap = 2 * cap;
cout << cap << endl;
this->arr = (int *)realloc(arr, sizeof(int)*cap);
return(arr ? true : false);
}
If realloc() fails it will return nullptr and the only pointer (arr) to the original memory region will be gone. Also, instead of return(arr ? true : false); you should simply use return arr != nullptr;.
The righttm way to use realloc():
bool stack::meminc()
{
int *temp = (int*) realloc(arr, sizeof(*temp) * cap * 2);
if(!temp)
return false;
cap *= 2;
arr = temp;
return true;
}
Also, where is your copy-ctor, assignment operator and d-tor?
The full function is incorrect. It should be
bool stack::full()
{
return(top == cap - 1 ? true : false);
}
or more simply and with added const
bool stack::full() const
{
return top == cap - 1;
}
Also you are using the top variable incorrectly. Since top starts at -1 you should increment top before you set the value, not afterwards
arr[++top] = x;
Not a bug, but from a design perpective meminc should be a private function.
I am attempting to implement the Tower of Hanoi iterative solution in c++ as noted in Wikepedia
Simpler statement of iterative solution
Alternating between the smallest and the next-smallest disks, follow the steps for the appropriate case:
For an even number of disks:
*make the legal move between pegs A and B*
*make the legal move between pegs A and C*
*make the legal move between pegs B and C*
*repeat until complete*
For an odd number of disks:
*make the legal move between pegs A and C*
*make the legal move between pegs A and B*
*make the legal move between pegs C and B*
*repeat until complete*
In each case, a total of 2n-1 moves are made.
The code I have written thus far is
#include <iostream>
#include <list>
const int SIZE = 5;
int pCount = 1;
using namespace std;
list<int> *lhs;
list<int> *mid;
list<int> *rhs;
void initTower(int size);
void printPeg(list<int> p);
bool printTower();
bool isEven(list<int> l);
bool move(list<int> *from, list<int> *to);
int main() {
lhs = new list<int>;
mid = new list<int>;
rhs = new list<int>;
initTower(SIZE);
printTower();
bool run = true;
while (run) {
int n = SIZE;
if (n % 2 == 0) // even
{
move(lhs,mid);
move(lhs,rhs);
move(mid,rhs);
}else{
move(lhs,rhs);
move(lhs,mid);
move(rhs,mid);
}
if (rhs->size() == SIZE) {
run = false;
}
}
return 0;
}
bool isEven(list<int> l) {
return l.size() % 2 == 0;
}
void initTower(int size) {
while (size--)
lhs->push_back(size + 1);
}
void printPeg(list<int> p) {
if (p.empty()) {
cout << "empty" << endl;
} else {
for (int i: p)
cout << i << " ";
cout << endl;
}
}
bool printTower() {
cout << "==============" << endl;
cout << "=====top=======" << pCount++ << endl;
printPeg(*lhs);
printPeg(*mid);
printPeg(*rhs);
cout << "==============" << endl << endl;
return true;
}
bool move(list<int> *from, list<int> *to) {
bool vailidMove = false;
int fVal = 0;
int toVal = 0;
if (!from->empty())
fVal = from->back();
if (!to->empty())
toVal = to->back();
if ((fVal < toVal || toVal == 0) && (fVal > 0 && fVal != 0)) {
from->pop_back();
to->push_back(fVal);
vailidMove = true;
printTower();
}
return vailidMove;
}
my output to the above program is.
==============
=====top=======1
5 4 3 2 1
empty
empty
==============
==============
=====top=======2
5 4 3 2
empty
1
==============
==============
=====top=======3
5 4 3
2
1
==============
==============
=====top=======4
5 4 3
2 1
empty
==============
==============
=====top=======5
5 4
2 1
3
==============
What am I overlooking? Any advise is helpful.
I added one condition in your move function to move poles if fVal > toVal (or you would stop instead of finishing the algorithm).
I alternated the source and destination half the time, as this is said in the wiki article you were referring to.
Alternating between the smallest and the next-smallest disks
I also changed the pCount initialization to 0 instead of 1 as the first print only list the starting tower and is not an operation. But you may put 1 again if that if what you wanted.
PS: I tested this code and it works perfectly fine, giving 2^n-1 operations like it is supposed to.
#include <iostream>
#include <list>
const int SIZE = 12;
int pCount = 0;
using namespace std;
list<int> *lhs;
list<int> *mid;
list<int> *rhs;
void initTower(int size);
void printPeg(list<int> p);
bool printTower();
bool isEven(list<int> l);
bool move(list<int> *from, list<int> *to);
int main() {
lhs = new list<int>;
mid = new list<int>;
rhs = new list<int>;
initTower(SIZE);
printTower();
bool run = true;
bool lowest = false;
while (run) {
lowest = !lowest;
int n = SIZE;
if (n % 2 == 0) // even
{
if (lowest){
move(lhs,mid);
if (rhs->size() == SIZE) {
break;
}
move(lhs,rhs);
move(mid,rhs);
}else{
move(mid,lhs);
if (rhs->size() == SIZE) {
break;
}
move(rhs,lhs);
move(rhs,mid);
}
}else{
if (lowest){
move(lhs,rhs);
move(lhs,mid);
if (rhs->size() == SIZE) {
break;
}
move(mid,rhs);
}else{
move(rhs,lhs);
move(mid,lhs);
if (rhs->size() == SIZE) {
break;
}
move(rhs,mid);
}
}
lowest = !lowest;
}
return 0;
}
bool isEven(list<int> l) {
return l.size() % 2 == 0;
}
void initTower(int size) {
while (size--)
lhs->push_back(size + 1);
}
void printPeg(list<int> p) {
if (p.empty()) {
cout << "empty" << endl;
} else {
for (int i: p)
cout << i << " ";
cout << endl;
}
}
bool printTower() {
cout << "==============" << endl;
cout << "=====top=======" << pCount++ << endl;
printPeg(*lhs);
printPeg(*mid);
printPeg(*rhs);
cout << "==============" << endl << endl;
return true;
}
bool move(list<int> *from, list<int> *to) {
bool vailidMove = false;
int fVal = 0;
int toVal = 0;
if (!from->empty())
fVal = from->back();
if (!to->empty())
toVal = to->back();
if ((fVal < toVal || toVal == 0) && fVal > 0) {
from->pop_back();
to->push_back(fVal);
vailidMove = true;
printTower();
}else if ((fVal > toVal || fVal == 0) && (toVal > 0 && toVal != 0)) {
from->push_back(toVal);
to->pop_back();
vailidMove = true;
printTower();
}
return vailidMove;
}
Here is a program with my Stack class and some another functions.
ReadTheFile() - reads numbers, which are stored in num_file.txt, and returns a vector with those numbers.
IntervalCheck() - adds the numbers of the specific range from input vector and returns a vector with those numbers only.
VecToMyStack() - adds numbers from a vector to a stack.
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#define STACK_EMPTY -1
#define OUT_OF_STACK -2
using namespace std;
template <class T>
class Stack {
private:
struct Node{
T element;
Node *prevElement;
};
size_t NumberOfElements;
Node *tempAdr;
Node *topElement;
Node *newElement;
Node *erasedElement;
public:
Stack(){
topElement = new Node;
topElement->prevElement = nullptr;
NumberOfElements = 0;
}
~Stack(){
cout << endl << "I'm a destructor";
while(NumberOfElements !=0 ){
tempAdr = topElement->prevElement;
delete topElement;
topElement = tempAdr;
NumberOfElements--;
}
delete topElement;
}
void push(T input_element){
tempAdr = topElement;
topElement = new Node;
topElement->element = input_element;
topElement->prevElement = tempAdr;
NumberOfElements++;
}
void pop(){
if (NumberOfElements == 0) throw STACK_EMPTY;
else {
tempAdr = topElement->prevElement;
delete topElement;
topElement = tempAdr;
NumberOfElements--;
}
}
T top(){
return NumberOfElements != 0 ? topElement->element : throw STACK_EMPTY;
}
void insert(size_t position, T input_element){
if (position >= NumberOfElements) throw OUT_OF_STACK;
else {
tempAdr = topElement;
for (size_t i = 0; i < position; i++){
tempAdr = tempAdr->prevElement;
}
newElement = new Node;
newElement->element = input_element;
newElement->prevElement = tempAdr->prevElement;
tempAdr->prevElement = newElement;
NumberOfElements++;
}
}
void erase(size_t position){
if (position >= (NumberOfElements-1)) throw OUT_OF_STACK;
else{
tempAdr = topElement;
for (size_t i = 0; i < position; i++){
tempAdr = tempAdr->prevElement;
}
erasedElement = tempAdr->prevElement;
tempAdr->prevElement = tempAdr->prevElement->prevElement;
delete erasedElement;
NumberOfElements--;
}
}
void print(){
if (NumberOfElements != 0){
tempAdr = topElement;
for (size_t i = 0; i < NumberOfElements; i++){
cout << tempAdr->element << " ";
tempAdr = tempAdr->prevElement;
}
}
}
size_t size() { return NumberOfElements; }
};
vector<int> ReadTheFile() {
vector<int> vec_from_file;
int buffer;
ifstream basefile;
basefile.open("num_file.txt", ios::in);
if (basefile.is_open()) {
do {
if (basefile >> buffer)
vec_from_file.push_back(buffer);
else {
basefile.clear();
basefile.ignore(1, ' ');
}
} while (!basefile.eof());
basefile.close();
}
else cout << "Unable to open file" << endl;
return vec_from_file;
}
vector<int> IntervalCheck(vector<int> vec_for_check){
vector<int> out_vec;
if (vec_for_check.empty()) cout << "There is nothing to check";
else {
int begin_int, end_int;
do {
cin.clear();
cin.sync();
cout << "Input the first and the last value of the interval: ";
cin >> begin_int >> end_int;
} while (cin.fail());
for (auto &k : vec_for_check)
if (k > begin_int && k < end_int)
out_vec.push_back(k);
}
return out_vec;
}
Stack<int> VecToMyStack(vector<int> input_vec){
Stack<int> output_st;
if (input_vec.empty()) {
cout << "the end";
}
else {
for (auto &k : input_vec){
output_st.push(k);
}
}
return output_st;
}
int main(){
int choice = 0;
do {
cin.clear();
cin.sync();
VecToMyStack(IntervalCheck(ReadTheFile())).print();
cout << "Would you like to measure another interval? 1-yes 2-no";
cin >> choice;
} while (choice == 1);
system("pause");
return 0;
}
The whole program should push numbers from the file to a stack, and print this stack, using the print() method of the class. For example, if there is a num_file.txt with
0 1 2 3 4 5 6 7 8 9 10
inside, the program is expected to work in that way:
Input the first and the last value of the interval: 0 10 /* zero and
ten are inputed by the user*/
1 2 3 4 5 6 7 8 9
Would you like to measure another interval? 1-yes 2-no
But when the VecToMyStack(IntervalCheck(ReadTheFile())).print(); line is executed, I'm getting
Access violation reading location 0xFEEEFEEE.
exception. It seemes like the destructor of my Stack class is running before the print()function. Why does that happen? Is there something special what I should add to my Stack class or to VecToMyStack() function?
Finally, after a couple of hours of research, I've got that missing peace of code:
Stack(const Stack &object){
tempAdr = object.topElement;
T * tempMas=new T[object.NumberOfElements];
for (size_t i = 0; i < object.NumberOfElements; i++){
tempMas[i] = tempAdr->element;
tempAdr = tempAdr->prevElement;
}
topElement = new Node;
topElement->prevElement = nullptr;
NumberOfElements = 0;
for (int i = object.NumberOfElements - 1; i >= 0; i--){
push(tempMas[i]);
}
delete[] tempMas;
}
I know that my Stack class is still uncomplete without the overloaded assignment operator, but at least my code runs fine.