Deleting an element from an array of pointers - c++

I'm creating a custom vector class as part of a homework assignment. What I am currently trying to do is implement a function called erase, which will take an integer as an argument, decrease my array length by 1, remove the element at the position specified by the argument, and finally shift all the elements down to fill in the gap left by "erased" element.
What I am not completely understanding, due to my lack of experience with this language, is how you can delete a single element from an array of pointers.
Currently, I have the following implemented:
void myvector::erase(int i)
{
if(i != max_size)
{
for(int x = i; x < max_size; x++)
{
vec_array[x] = vec_array[x+1];
}
vec_size --;
//delete element from vector;
}
else
//delete element from vector
}
The class declaration and constructors look like this:
template <typename T>
class myvector
{
private:
T *vec_array;
int vec_size;
int max_size;
bool is_empty;
public:
myvector::myvector(int max_size_input)
{
max_size = max_size_input;
vec_array = new T[max_size];
vec_size = 0;
}
I have tried the following:
Using delete to try and delete an element
delete vec_size[max_size];
vec_size[max_size] = NULL;
Setting the value of the element to NULL or 0
vec_size[max_size] = NULL
or
vec_size[max_size] = 0
None of which are working for me due to either operator "=" being ambiguous or specified type not being able to be cast to void *.
I'm probably missing something simple, but I just can't seem to get passed this. Any help would be much appreciated. Again, sorry for the lack of experience if this is something silly.

If your custom vector class is supposed to work like std::vector, then don't concern yourself with object destruction. If you need to erase an element, you simply copy all elements following it by one position to the left:
void myvector::erase(int i)
{
for (int x = i + 1; x < vec_size; x++) {
vec_array[x - 1] = vec_array[x];
}
vec_size--;
}
That's all the basic work your erase() function has to do.
If the elements happen to be pointers, you shouldn't care; the user of your vector class is responsible for deleting those pointers if that's needed. You cannot determine if they can actually be deleted (the pointers might point to automatic stack variables, which are not deletable.)
So, do not ever call delete on an element of your vector.
If your vector class has a clear() function, and you want to make sure the elements are destructed, simply:
delete[] vec_array;
vec_array = new T[max_size];
vec_size = 0;
And this is how std::vector works, actually. (Well, the basic logic of it; of course you can optimize a hell of a lot of stuff in a vector implementation.)

Since this is homework i wont give you a definitive solution, but here is one method of erasing a value:
loop through and find value specified in erase function
mark values position in the array
starting from that position, move all elements values to the previous element(overlapping 'erased' value)
for i starting at position, i less than size minus one, i plus plus
element equals next element
reduce size of vector by 1
see if this is a big enough hint.

Related

How can I correctly push back a series of objects in a vector in C++?

The scope of the program is to create a Container object which stores in a vector Class objects. Then I want to print, starting from a precise Class object of the vector all its predecessors.
class Class{
public:
Class(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
}
}
};
~Class();
void setName(const int& n);
void setNext( vector<Class>& vec, const int& pos);
Class* getNext();
string getName();
void printAllNext(){ //print all next Class objects including himself
cout << _name <<endl;
if (_next != nullptr) {
(*_next).printAllNext();
}
}
private:
Class* _next;
string _name;
};
class Container{
public:
Container(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
};
~Container();
void printFromVec(const int& n){//print all objects of _vec starting from n;
_vec[n].printAllNext();
};
private:
vector<Class> _vec;
};
int main() {
Container c;
c.printFromVec(5);
}
The problem is that all _next pointers of Class objects are undefined or random.
I think the problem is with this part of code:
class Container{
public:
Container(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
};
Debugging I noticed that pointers of already created objects change their values.
What is the problem? How can I make it work?
Although there is really error in the code (likely wrong copypaste), the problem is really following: std::vector maintains inside dynamically allocated array of objects. It starts with certain initial size. When you push to vector, it fills entries of array. When all entries are filled but you attempt pushing more elements, vector allocates bigger chunk of memory and moves or copies (whichever you element data type supports) objects to a new memory location. That's why address of object changes.
Now some words on what to do.
Solution 1. Use std::list instead of std::vector. std::list is double linked list, and element, once added to list, will be part of list item and will not change its address, there is no reallocation.
Solution 2. Use vector of shared pointers. In this case you will need to allocate each object dynamically and put address into shared pointer object, you can do both at once by using function std::make_shared(). Then you push shared pointer to vector, and store std::weak_ptr as pointer to previous/next one.
Solution 3. If you know maximum number of elements in vector you may ever have, you can leave all as is, but do one extra thing before pushing very first time - call reserve() on vector with max number of elements as parameters. Vector will allocate array of that size and keep it until it is filled and more space needed. But since you allocated maximum possible size you expect to ever have, reallocation should never happen, and so addresses of objects will remain same.
Choose whichever solution you think fits most for your needs.
#ivan.ukr Offered a number of solutions for keeping the pointers stable. However, I believe that is the wrong problem to solve.
Why do we need stable pointers? So that Class objects can point to the previous object in a container.
Why do we need the pointers to previous? So we can iterate backwards.
That’s the real problem: iterating backwards from a point in the container. The _next pointer is an incomplete solution to the real problem which is iteration.
If you want to iterate a vector, use iterators. You can read about them on the cppreference page for std::vector. I don’t want to write the code for you but I’ll give you some hints.
To get an iterator referring to the ith element, use auto iter = _vec.begin() + i;.
To print the object that this iterator refers to, use iter->print() (you’ll have to rename printAllNext to print and have it just print this object).
To move an iterator backwards, use --iter.
To check if an iterator refers to the first element, use iter == _vec.begin().
You could improve this further by using reverse iterators but I’ll leave that up to you.

Problem implementing a dynamically-sized array

I'm writing an implementation of a dynamically-sized array. The code compiles without errors, but the array elements don't get copied properly. They seem to just get erased (overwritten with 0's). Trying to call a getter on an array element causes a segfault.
The array holds pointers to some basic class objects; this is the main difference between my code and the examples I looked up.
This is the function:
// Pointer to array of pointers
SomeClass** mainArray = new SomeClass[1];
int numItems = 0;
void AddItemDynamic(SomeClass* newVal) {
SomeClass** tempArray = new SomeClass*[numItems+1];
// Copying pointers to bigger array
for (int i = 0; i < numItems - 1; i++) {
tempArray[i] = mainArray[i];
}
numItems++;
// Adding the new value
tempArray[numItems] = newVal;
delete [] mainArray;
mainArray = tempArray;
}
The code should copy the array elements over, then reassign the pointer to the newly created array. Instead, the pointer seems to be set to something else.
If the current array have numItems element in them, then the loop
for (int i = 0; i < numItems - 1; i++)
will copy one less than numItems elements.
And when you add the new element, you go out of bounds of the new array, because you increase numItems to early.
So two off-by-one errors in the same function, one in each direction.
And as mentioned in a comment (thanks Ayxan) the first off-by-one error will mean that the first two times you call this function, the copying loop won't happen. That's actually good when doing it the first time as then there's nothing to copy, but the second time there should be something to copy and yet the loop (currently) won't run.

How to create an auxiliary data structure to keep track of heap indices in a minheap for the decrease_key operation in c++

I think this is probably a trivial problem to solve but I have been struggling with this for past few days.
I have the following vector: v = [7,3,16,4,2,1]. I was able to implement with some help from google simple minheap algorithm to get the smallest element in each iteration. After extraction of the minimum element, I need to decrease the values of some of the elements and then bubble them up.
The issue I am having is that I want find the elements whose value has to be reduced in the heap in constant time, then reduce that value and then bubble it up.
After the heapify operation, the heap_vector v_h looks like this: v_h = [1,2,7,4,3,16]. When I remove the min element 1, then the heap vector becomes, [2,3,7,4,16]. But before we do the swap and bubble up, say I want to change the values of 7 to 4, 16 to 4 and 4 to 3.5 . But I am not sure where they will be in the heap. The indices of values of the elements that have to be decreased will be given with respect to the original vector v. I figured out that I need to have an auxiliary data structure that can keep track of the heap indices in relation to the original order of the elements (the heap index vector should look like h_iv = [2,4,5,3,1,0] after all the elements have been inserted into the minheap. And whenever an element is deleted from the minheap, the heap_index should be -1. I created a vector to try to update the heap indices whenever there is a change but I am unable to do it.
I am pasting my work here and also at https://onlinegdb.com/SJR4LqQO4
Some of the work I had tried is commented out. I am unable to map the heap indices when there is a swap in the bubble up or bubble down operations. I will be very grateful to anyone who can lead me in a direction to solve my problem. Please also let me know if I have to rethink some of my logic.
The .hpp file
#ifndef minheap_hpp
#define minheap_hpp
#include <stdio.h>
// #include "helper.h"
#include <vector>
class minheap
{
public:
std::vector<int> vect;
std::vector<int> heap_index;
void bubble_down(int index);
void bubble_up(int index);
void Heapify();
public:
minheap(const std::vector<int>& input_vector);
minheap();
void insert(int value);
int get_min();
void delete_min();
void print_heap_vector();
};
#endif /* minheap_hpp */
The .cpp file
#include "minheap.hpp"
minheap::minheap(const std::vector<int>& input_vector) : vect(input_vector)
{
Heapify();
}
void minheap::Heapify()
{
int length = static_cast<int>(vect.size());
// auto start = 0;
// for (auto i = 0; i < vect.size(); i++){
// heap_index.push_back(start);
// start++;
// }
for(int i=length/2-1; i>=0; --i)
{
bubble_down(i);
}
}
void minheap::bubble_down(int index)
{
int length = static_cast<int>(vect.size());
int leftChildIndex = 2*index + 1;
int rightChildIndex = 2*index + 2;
if(leftChildIndex >= length){
return;
}
int minIndex = index;
if(vect[index] > vect[leftChildIndex])
{
minIndex = leftChildIndex;
}
if((rightChildIndex < length) && (vect[minIndex] > vect[rightChildIndex]))
{
minIndex = rightChildIndex;
}
if(minIndex != index)
{
std::swap(vect[index], vect[minIndex]);
// std::cout << "swap " << index << " - " << minIndex << "\n";
// auto a = heap_index[heap_index[index]];
// auto b = heap_index[heap_index[minIndex]];
// heap_index[a] = b;
// heap_index[b] = a;
// print_vector(heap_index);
bubble_down(minIndex);
}
}
void minheap::bubble_up(int index)
{
if(index == 0)
return;
int par_index = (index-1)/2;
if(vect[par_index] > vect[index])
{
std::swap(vect[index], vect[par_index]);
bubble_up(par_index);
}
}
void minheap::insert(int value)
{
int length = static_cast<int>(vect.size());
vect.push_back(value);
bubble_up(length);
}
int minheap::get_min()
{
return vect[0];
}
void minheap::delete_min()
{
int length = static_cast<int>(vect.size());
if(length == 0)
{
return;
}
vect[0] = vect[length-1];
vect.pop_back();
bubble_down(0);
}
void minheap::print_heap_vector(){
// print_vector(vect);
}
and the main file
#include <iostream>
#include <iostream>
#include "minheap.hpp"
int main(int argc, const char * argv[]) {
std::vector<int> vec {7, 3, 16, 4, 2, 1};
minheap mh(vec);
// mh.print_heap_vector();
for(int i=0; i<3; ++i)
{
auto a = mh.get_min();
mh.delete_min();
// mh.print_heap_vector();
std::cout << a << "\n";
}
// std::cout << "\n";
return 0;
}
"I want to change the values of 7 to 4, 16 to 4 and 4 to 3.5 . But I am not sure where they will be in the heap. The indices of values of the elements that have to be decreased will be given with respect to the original vector v. ... Please also let me know if I have to rethink some of my logic."
Rather than manipulate the values inside the heap, I would suggest keeping the values that need changing inside a vector (possibly v itself). The heap could be based on elements that are a struct (or class) that holds an index into the corresponding position in the vector with the values, rather than hold the (changing) value itself.
The struct (or class) would implement an operator< function that compares the values retrieved from the two vector locations for the respective index values. So, instead of storing the comparison value in the heap elements and comparing a < b, you would store index positions i and j and so on and compare v[i] < v[j] for the purpose of heap ordering.
In this way, the positions of the numerical values you need to update will never change from their original positions. The position information will never go stale (as I understand it from your description).
Of course, when you make changes to those stored values in the vector, that could easily invalidate any ordering that might have existed in the heap itself. As I understand your description, that much was necessarily true in any case. Therefore, depending on how you change the values, you might need to do a fresh make_heap to restore proper heap ordering. (That isn't clear, since it depends on whether your intended changes violate heap assumptions, but it would be a safe thing to assume unless there are strong assurances otherwise.)
I think the rest is pretty straight forward. You can still operate the heap as you intended before. For ease you might even give the struct (or class) a lookup function to return the current value at it's corresponding position in the vector, if you need that (rather than the index) as you pop out minimum values.
p.s. Here is a variation on the same idea.
In the original version above, one would likely need to also store a pointer to the location of the vector that held the vector of values, possibly as a shared static pointer of that struct (or class) so that all the members could dereference the pointer to that vector in combination with the index values to look up the particular member associated with that element.
If you prefer, instead of storing that shared vector pointer and an index in each member, each struct (or class) instance could more simply store a pointer (or iterator) directly to the corresponding value's location. If the values are integers, the heap element struct's member value could be int pointer. While each pointer might be larger than an index value, this does have the advantage that it eliminates any assumption about the data structure that holds the compared values and it is even simpler/faster to dereference vs. lookup with an index into the vector. (Both are constant time.)
One caution: In this alternate approach, the pointer values would be invalidated if you were to cause the vector's storage positions to change, e.g. by pushing in new values and expanding it in a way that forces it to reallocate it's space. I'm assuming you only need to change values, not expand the number of values after you've begun to use the heap. But if you did need to do that, that would be one reason to prefer index values, since they remain valid after expanding the vector (unlike pointers).
p.p.s. This technique is also valuable when the objects that you want to compare in the heap are large. Rather than have the heap perform many copy operations on large objects as it reorders the positions of the heap elements, by storing only pointers (or index values) the copying is much more efficient. In fact, this makes it possible to use heaps on objects that you might not want to copy at all.
Here is a quick idea of one version of the comparison function (with some class context now added).
class YourHeapElementClassName
{
public:
// constructor
explicit YourHeapElementClassName(theTypeOfYourComparableValueOrObject & val)
: m_valPointer(&val)
{
}
bool operator<(const YourHeapElementClassName & other) const
{
return *m_valPointer < *(other.m_valPointer);
}
...
private:
theTypeOfYourComparableValueOrObject * m_valPointer;
}; // YourHeapElementClassName
// and later instead of making a heap of int or double,
// you make a heap of YourHeapElementClassName objects
// that you initialize so each points to a value in v
// by using the constructor above with each v member.
// If you (probably) don't need to change the v values
// through these heap objects, the member value could be
// a pointer to a const value and the constructor could
// have a const reference argument for the original value.
If you had need to do this with different types of values or objects, the pointer approach could be implemented with a template that generalizes on the type of value or object and holds a pointer to that general type.

How to copy a set of object to an array of object?

I have to copy the first size element from a set of Solution (a class) named population to an array of solution named parents. I have some problems with iterators because i should do an hybrid solution between a normal for loop
and a for with iterators. The idea is this: when I'm at the ith iteration of the for I declare a new iterator that's pointing the beginning
of population, then I advance this iterator to the ith position, I take this solution element and I copy into parents[i]
Solution* parents; //it is filled somewhere else
std::set<Solution> population; //it is filled somewhere else
for (int i = 0; i < size; i++) {
auto it = population.begin();
advance(it, i);
parents[i] = *it;
}
Two error messages popup with this sentence: 'Expression: cannot dereference end map/set iterator'
and 'Expression: cannot advance end map/set iterator'
Any idea on how to this trick? I know it's kinda bad mixing array and set, i should use vector instead of array?
You use std::copy_n.
#include <algorithm>
extern Solution* parents; //it is filled somewhere else
extern std::set<Solution> population; //it is filled somewhere else
std::copy_n(population.begin(), size, parents);
It seems like size may be incorrectly set. To ensure that your code behaves as expected, you should just use the collection's size directly:
auto it = population.begin();
for (int i = 0; i < population.size(); i++) {
parents[i] = *it;
++it;
}
This can also be solved with a much simpler expression:
std::copy(population.begin(), population.end(), parents);
I have to copy the first size element from a set [..] to an array
You can use std::copy_n.
for (int i = 0; i < size; i++) {
auto it = population.begin();
advance(it, i);
The problem with this is that you're iterating over the linked list in every iteration. This turns the copy operation from normally linear complexity to quadratic.
Expression: cannot dereference end map/set iterator'
The problem here appears to be that your set doesn't contain at least size number of elements. You cannot copy size number of elements if there aren't that many. I suggest that you would copy less elements when the set is smaller.
i should use vector instead of array?
Probably. Is the array very large? Is the size of the vector not known at compile time? If so, use a vector.

Container Template Class - Decreasing Container Size

I've written some code to decrease the capacity of a templated container class. After an element is removed from the container, the erase function checks to see whether or not 25% of the total space is in use, and whether reducing the capacity by half would cause it to be less than the default size I've set. If these two return true, then the downsize function runs. However, if this happens while I'm in the middle of a const_iterator loop, I get a segfault.
edit again: I'm thinking it's because the const_iterator pointer is pointing to the old array and needs to be pointed to the new one created by downsize()...now how to go about doing that...
template <class T>
void sorted<T>::downsize(){
// Run the same process as resize, except
// in reverse (sort of).
int newCapacity = (m_capacity / 2);
T *temp_array = new T[newCapacity];
for (int i = 0; i < m_size; i++)
temp_array[i] = m_data[i];
// Frees memory, points m_data at the
// new, smaller array, sets the capacity
// to the proper (lower) value.
delete [] m_data;
m_data = temp_array;
setCap(newCapacity);
cout << "Decreased array capacity to " << newCapacity << "." << endl;
}
// Implementation of the const_iterator erase method.
template <class T>
typename sorted<T>::const_iterator sorted<T>::erase(const_iterator itr){
// This section is reused from game.cpp, a file provided in the
// Cruno project. It handles erasing the element pointed to
// by the constant iterator.
T *end = &m_data[m_capacity]; // one past the end of data
T *ptr = itr.m_current; // element to erase
// to erase element at ptr, shift elements from ptr+1 to
// the end of the array down one position
while ( ptr+1 != end ) {
*ptr = *(ptr+1);
ptr++;
}
m_size--;
// Once the element is removed, check to
// see if a size reduction of the array is
// necessary.
// Initialized some new values here to make
// sure downsize only runs when the correct
// conditions are met.
double capCheck = m_capacity;
double sizeCheck = m_size;
double usedCheck = (sizeCheck / capCheck);
int boundCheck = (m_capacity / 2);
if ((usedCheck <= ONE_FOURTH) && (boundCheck >= DEFAULT_SIZE))
downsize();
return itr;
}
// Chunk from main that erases.
int i = 0;
for (itr = x.begin(); itr != x.end(); itr++) {
if (i < 7) x.erase(itr);
i++;
}
To prevent issues with invalidated iterators during an erase loop, you can use:
x.erase(itr++);
instead of separate increment and increment calls (obviously if you're not erasing everything you loop over you'd need an else case to increment past the non-erased items.) Note this is a case where you need to use post-increment rather than pre-increment.
The whole thing looks a bit inefficient though. The way you convert to an array suggests that it's only going to work with certain container types anyway. If you expect lots of erasing from the middle of the container you might be able to avoid the memory issue by just choosing a different container; list maybe.
If you choose vector, you might find that calling shrink_to_fit is what you want.
Also note that erase has a return value that will point to the (new) location of the element after the erased one.
If you go down the line of returning an iterator to a newly created down-sized version of the container, note that comparing to the original containers end() iterator wouldn't work.
I figured out the segfault problem!
What was going on was that my x.end() was using the size of the array (NOT the capacity), where the size is the number of data elements stored in the array, instead of the capacity which is actually the size of the array. So when it was looking for the end of the array, it was seeing it as numerous elements before the actual end.
Now on to more interesting problems!