How to implement erase on vector in c++ - c++

Learning from Accelerated C++: Practical Programming by Example, in chapter 11, there was an implementation (only with basic features) of vector container from STL. After that was an exercise for implementing erase function just as std::vector does. What I have tried:
#include <memory>
template<class T>
class Vec{
private:
T *data;
T *avail;
T *limit;
std::allocator<T> alloc;
...
public:
explicit Vec(size_t n, const T &val = T())
{
create(n, val);
}
T *const begin()
{
return data;
}
T *const end()
{
return avail;
}
T *erase(T* const pos);
...
};
template <class T>
void Vec<T>::create(size_t n, const T &val)
{
data = alloc.allocate(n);
limit = avail = data + n;
std::uninitialized_fill(data, limit, val);
}
// here I am trying to implement the erase function with 3 pointers (data, avail, limit)
template<class T>
T* Vec<T>::erase(T *const i)
{
if(i==end())
{
return end();
}
else if(i >= begin() && i < end())
{
size_t member = i-data;
size_t size = limit-data;
T* new_data = alloc.allocate(size);
std::uninitialized_copy(data, i, new_data);
T* new_avail = std::uninitialized_copy(i+1, avail, i);
data = new_data;
avail = new_avail;
limit = data + size;
return &data[member];
}
else
{
return 0;
}
}
Now If I want to check, if that function works correctly:
#include "vec.hpp"
int main()
{
Vec<int> v(5, 2);
if (v.erase(v.begin()+2))
{
for (int i:v)
{
cout << i << endl;
}
}
}
I get
...
0
0
0
0
Segmentation fault
I have somehow made infinity allocation-loop, but I have no idea how. Anyway, How can I make the erase function (or in another words, how to shift elements after the erased one to left), via std::uninitialized_copy?
EDIT:
the whole class definition is there:
https://www.codepile.net/pile/rLmz8wRq

Here you create new storage for vector:
T* new_data = alloc.allocate(size);
and here you write to array pointed by argument, which (supposedly) points to location on old storage. new_avail would point to old storage.
T* new_avail = std::uninitialized_copy(i+1, avail, i);
^ that's destination
^ that's source
Then you even leak memory:
data = new_data; // old storage pointed by data is lost along with the "tail" of array
After this vector state is completely broken, pointer arithmetic go to undefined area:
avail = new_avail; // avail points to old storage, data points to new one.
// (data < avail) no longer guaranteed to be true
Because in all likelihood data would be greater than avail, you don't get an infinite loop, you may get a very long one. OR may not. Attempt to iterate through vector after this "erase" amounts to Undefined Behavior.

Related

I can't understand how to solve problem with memory leak C++

Condition
In lectures, we have already started to implement our vector. In this task, you need to develop it: add the Size, Capacity, and PushBack methods. Send the simple_vector.h header file containing the SimpleVector class template declaration and definition for verification:
Requirements:
the Capacity method should return the current capacity of the vector — the number of elements that fit into the memory block currently allocated by the vector
the Size method must return the number of elements in the vector
the PushBack method adds a new element to the end of the vector; if there is no free space left in the current allocated memory block (i.e. Size() == Capacity()), the vector must allocate a block of size 2 * Capacity(), copy all the elements to it, and delete the old one.
the first call to the PushBack method for a newly created object must make the capacity equal to one
the Push Back method must have a amortized constant complexity
the begin and end methods must return iterators the current beginning and end of the vector
the current memory block allocated by the vector must be freed in the destructor
also see the attached solution template for additional requirements for working with SimpleVector in unit tests.
The preparation of the decision:
simple_vector.h: https://d3c33hcgiwev3.cloudfront.net/q-OL4qX_EeilzRLZf2WxfA_ac4e8270a5ff11e89fd0455a8819d387_simple_vector.h?Expires=1596067200&Signature=cLfBpytTripoqpOYaW9g4~2-JqTI~8HtxahNwNATwBeq28RdXCvkcqghN~UUPv~wx1XZTVOTs8JDsZQjEALk6Soy70QFADkK9lSfFpLNcQq-Dxd4oxk-C5QDEhadM1LrVGe8Rmz0jRYgIV5sDTvAATBhiY3k-KqbAaDe1AK6QiE_&Key-Pair-Id=APKAJLTNE6QMUY6HBC5A
simple_vector.cpp: https://d3c33hcgiwev3.cloudfront.net/uoPvEoauEeianAr0yIdmDg_bae6cec086ae11e88d9327752d64e780_simple_vector.cpp?Expires=1596067200&Signature=CE1Mox1yU6LjGDXL1xstxT9anv9NI~otNwhBw5AbPyLBquRIi9E6cotR~BQsrvU-djoksfjV9YgnsyF00eFnVjsk~oF0z18wkVkgdIirPB-NNLH0aFvD4WFG97qmSuD0WjeetWyi6UR5BKYCnwfO~ax6-HZLM-GWheO9LHc~BvE_&Key-Pair-Id=APKAJLTNE6QMUY6HBC5A
Comment:
The header file that you send for verification should not include the <vector>, <list>, <forward_list>, <deque>, <map> files. If you have one of these files enabled, you will get a compilation error.
Hint:
For sure, your implementation of the SimpleVector class template will have a field that is a pointer. In the default constructor, you will need to initialize it with something. In the lectures, we only discussed one way to initialize pointers — using the new operator. In C++, there is a special value that means a pointer that points to nothing — nullptr:
int* p = nullptr;
string* q = nullptr;
map<string, vector<int>>* r = nullptr;
You can use nullptr to initialize the pointer in the default constructor.
How to send:
When the work is ready, you can upload files for each part of the task on the 'My work'tab.
And here is my .hsolution to which the Coursera testing system responds a 10 != 8: Memory leak detected. However I can't figure out where the leak is going. Help me pls.
#pragma once
#include <cstdlib>
using namespace std;
template <typename T>
class SimpleVector {
public:
SimpleVector()
: data(nullptr)
, end_(data)
, size_(0) {}
explicit SimpleVector(size_t size)
: data(new T[size])
, end_(data + size)
, size_(size) {}
~SimpleVector() {
delete[] data;
}
T& operator[](size_t index) { return data[index]; }
T* begin() const { return data; }
T* end() const { return end_; }
size_t Capacity() const { return end_ - data; }
size_t Size() const { return size_; }
void PushBack(const T& value) {
if (size_ == Capacity()) {
if (size_ == 0) {
delete[] data;
data = new T[1];
data[size_] = value;
++size_;
end_ = data + size_;
}
else {
T* local_data = new T[size_];
for (size_t i = 0; i < size_; ++i) {
local_data[i] = data[i];
}
delete[] data;
data = new T[2 * Capacity()];
for (size_t i =0; i < size_; ++i) {
data[i] = local_data[i];
}
delete[] local_data;
data[size_] = value;
++size_;
end_ = data + size_ * 2;
}
}
else {
data[size_] = value;
size_++;
}
}
private:
T *data;
T *end_;
size_t size_;
};
Thank you in advance.
There is a memory leak in PushBack due to lack of exception safety. Consider:
T* local_data = new T[size_];
// potentially throwing operations here...
delete[] local_data;
If those operations throw, then delete[] local_data; will never be executed.
Typical way to avoid such memory leak is to use smart pointers instead of bare pointers for ownership. The antiquated way is to use try-catch.
Your class also fails to enforce the class invariant of uniqueness of data pointer. Such constraint is essential for the destructor to be correct, because an allocation must be deleted exactly once, and no more.
Making a copy of an instance of the class will result in undefined behaviour because of same pointer being deleted in multiple destructors. Another consequence is that the assigment operators will leak the previously allocated memory (before the UB occurs in the destructor):
{
SimpleVector vec(42);
SimpleVector another(1337);
SimpleVector vec = another; // memory leak in assignment operator
} // undefined behaviour in the destructor
The problem is in the copy and move constructors and assignment operators, which you've left as implicitly generated. The implicitly generated special member functions will copy the pointer value, violating its uniqueness (and failing to delete the previous allocation in case of assignment). In other words, those functions perform a shallow copy.
Using a smart pointer as the member is an easy solution. Otherwise, you must implement copy and move constructors and assignment operators that don't leak, nor violate uniqueness.
Note that even if you did use a smart pointer, you'd still need user defined copy etc. because of the end pointer. If you instead used an integer that is relative to data, then you could avoid defining those functions.
P.S. There is no need to allocate twice, and copy twice. Instead, allocate one larger buffer, copy the old one, delete the old, point to the new.
P.P.S. As a sidenote: The vector you are implementing behaves quite differently from the standard vector, which is probably intentional by your teacher. When I add an object to a vector of 10 elements, I would expect only one element to be created and possibly 10 be copied due to relocation, rather than 20 objects being created with 9 being unaccessible.
A proper implementation of vector separates the allocation of memory, and creation of objects into that memory which allows the growth of the memory to be geometric without creating objects until they are added into the vector. I suspect that how to do this is outside the scope of your exercise.
I wouldn't call it a leak, but you treat end_ inconsistently. It seems like you are treating Size and Capacity as equivalent values, they are not.
Either end_ should point one past the allocated (but not necessarily populated) memory, and you return data + size in end(), or it should point one past the last element, and you should store size_t capacity_ not size_t size_;
Here is solution without memory leak. Thank you.
#pragma once
#include <cstdlib>
using namespace std;
template <typename T>
class SimpleVector {
public:
SimpleVector() {
data_ = nullptr;
end_ = data_;
size_ = 0;
capacity_ = 0;
}
explicit SimpleVector(size_t size) {
data_ = new T[size];
end_ = data_ + size;
size_ = size;
capacity_ = size;
}
SimpleVector(const SimpleVector& that)
: data_(that.data_)
, end_(that.end_)
, size_(that.size_)
, capacity_(that.capacity_) {}
SimpleVector& operator = (const SimpleVector& that) {
data_ = that.data_;
end_ = that.end_;
size_ = that.size_;
capacity_ = that.capacity_;
}
~SimpleVector() { delete[] data_; }
T& operator[](size_t index) {
return data_[index];
}
T* begin() const { return data_; }
T* end() const { return data_ + size_; }
size_t Capacity() const { return capacity_; }
size_t Size() const { return size_; }
void PushBack(const T& value) {
if (size_ == capacity_) {
if (capacity_ == 0) { // т. е. создали конструктором по умолчанию, size_ = 0
data_ = new T[1];
capacity_ = 1;
data_[size_] = value;
++size_;
end_ = data_ + size_;
}
else if (capacity_ == size_) { // т. е. capacity_ == size_
T* local_data = new T[2 * size_];
for (size_t i = 0; i < size_; ++i) {
local_data[i] = data_[i];
}
delete[] data_;
data_ = new T[2 * size_];
for (size_t i = 0; i < size_; ++i) {
data_[i] = local_data[i];
}
delete[] local_data;
data_[size_] = value;
size_++;
capacity_ *= 2;
end_ = data_ + size_;
}
}
else {
data_[size_] = value;
size_++;
}
}
private:
T *data_;
T *end_;
size_t size_;
size_t capacity_;
};

Can I get index directly from pointer of vector element in C++?

I have a big vector container that holds around 300.000 object. Also I have pointers to these objects.
Are there any fast way to get index of object in vector with using pointer?
Since vectors are organized sequentially, you can get an index by subtracting pointer to initial element from the pointer to element in question:
std::vector<MyObject> vect;
MyObject *ptrX = ... // Pointer to element in question
ptrdiff_t index = ptrX - &vect[0];
Iterator header should be useful in that case.
Let's assume you have something like:
using Vector = std::vector<Foo>;
using Iterator = Vector::iterator;
Vector big_vector;
And now your have an iterator to an object:
Iterator p_obj = get_Theobj(big_vector);
The the index could be easily get with distance:
auto index = std::distance(big_vector.begin(), p_obj);
// Note: index's type is a `difference_type` aka ptrdiff_t (usually signed integer).
The powerful of using that approach is the versatility. Indeed, it works with "C-like vector", std::array, std::list, as well.
You may use std::distance
std::vector<Object> objects = /*..*/;
const Object *p = /* object[i] */;
std::ptrdiff_t index = std::distance(objects.data(), p);
// Now index == i.
Plenty of good answers here. Combining them together, here's a little library suite which allows computation of the index of an item by either pointer or reference.
As an added bonus, the caller may optionally supply a policy object, to be enacted if the element is not in the container.
#include <stdexcept>
#include <cassert>
#include <vector>
struct exception_policy
{
[[noreturn]]
std::size_t out_of_range() const
{
throw std::out_of_range("index_of_element");
}
};
struct assertion_policy
{
std::size_t out_of_range() const
{
assert(!"out of range");
return _fallback.out_of_range();
}
exception_policy _fallback {};
};
struct zero_policy
{
std::size_t out_of_range() const
{
return 0;
}
};
template<class T, class A, class Policy = exception_policy>
std::size_t index_of_element(std::vector<T, A> const& vec,
typename std::vector<T, A>::const_pointer pitem,
Policy policy = Policy{})
{
auto pbegin = vec.data();
auto pend = pbegin + vec.size();
if (pitem < pbegin or pitem >= pend)
{
return policy.out_of_range();
}
else
{
return std::distance(pbegin, pitem);
}
}
template<class T, class A, class Policy = exception_policy>
std::size_t index_of_element(std::vector<T, A> const& vec,
typename std::vector<T, A>::const_reference item, Policy policy = Policy{})
{
return index_of_element(vec, std::addressof(item), policy);
}
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8 };
auto px = std::addressof(v[5]);
auto& rx = *px;
try {
// use default policy of throwing out_of_range...
auto i = index_of_element(v, rx);
assert(i == 5);
}
catch(...)
{
assert(!"should not throw");
}
try {
auto i = index_of_element(v, px);
assert(i == 5);
}
catch(...)
{
assert(!"should not throw");
}
auto py = v.data() + 1000; // out of bounds
try {
auto i = index_of_element(v, py);
assert(!"should have thrown");
}
catch(std::out_of_range const& e)
{
// success
}
catch(...)
{
assert(!"should not throw this");
}
// specify a custom policy
auto i = index_of_element(v, ry, zero_policy());
assert(i == 0);
}
int getIndex(Object* pObject) {return pObject - &(my_vewctor[0]);}
Assuming C++11 at least:
You have some class Myclass; (hopefully a rather small one, since you have many instances of it).
You have some variable std::vector<Myclass> bigvec; and it is big enough so could have about half a million objects.
You have some valid pointer Myclass* ptr; and it could point inside an object from bigvec.
You might get its index in bigvec in int ix; (or better yet size_t ix;) using
if (ptr >= bigvec.data() && ptr < bigvec.data() + bigvec.size())
ix = ptr - bigvec.data();
However, be aware that you could have made copies of Myclass (e.g. thru assignment, passing by value some argument, etc...), and then a pointer to such a copy won't be in bigvec

End of array points to newly allocated array C++

Using C++, I am trying to create an array that holds pointers to objects I'm storing. But when the array is full, I want to expand the array.
the easy option is to allocate a new array with bigger size, then copy the elements to it, this is quite inefficient, and I thought of another way I want to try to do it:
create array of fixed size X
When full, create a new array, and make the end of the first array point to the start of the first element
Repeat as long as needed
What methods can I use to do that? I thought of one way to do it, but it seems very hacky:
declare all my new array as pointers to object pointer, then reinterprit_cast the filled elements to object pointer.
Note: I know I can use Vector, but I am instructed not to use std library.
Kind Regards,
There are some good answers in the comments already. I just want to provide a way to achieve exactly the behavior you described.
Since the elements of the array are pointers as well, you can define a union as the element of your array like this:
template<typename T>
union Cell
{
T* pElm;
Cell* pNext;//A fixed size array of Cells
}
And then build your array on top of it. For example:
template<typename T>
class SpecialArray
{
public:
//the next pointer is included
static const size_t ARRAY_LEN = 1000;// For example
using Pointer = T*;
using Segment = Cell<T>[ARRAY_LEN];
protected:
Segment* pFirst;
size_t mSize;
public:
SpecialArray()
:pFirst(nullptr),mSize(0){}
SpecialArray(SpecialArray&&){}
~SpecialArray(){}
Pointer& operator[](size_t index)
{
Segment* seg = pFirst;
size_t offest = 0;
//Search logic...
return seg[offest]->pElm;
}
const Pointer& operator[](size_t index) const;
};
Using C++, I am trying to create an array that holds pointers to
objects I'm storing. But when the array is full, I want to expand the
array.
With C++ templates and C primitives we can improvise a simple vector like below. And the grow buffer strategy is to double the size when the threshold is met.
#include <iostream>
#include <stdlib.h>
template <typename T>
class MyVector
{
public:
MyVector() : m_count(0), m_size(0), m_buffer(0)
{
m_size = bufInitSize;
m_buffer = (T*)malloc(sizeof(T) * bufInitSize);
}
~MyVector()
{
if (m_buffer)
free(m_buffer);
}
void add(const T& p)
{
if (m_count + 1 >= m_size)
{
m_size *= 2;
m_buffer = (T*)realloc(m_buffer, sizeof(T) * m_size);
}
m_buffer[m_count ++ ] = p;
}
T& operator[](int idx)
{
return m_buffer[idx];
}
private:
static const int bufInitSize = 1024;
T* m_buffer;
int m_count;
int m_size;
};
void main()
{
// using MyVector
MyVector<int*> vctOfIntPtr;
int n = 100;
vctOfIntPtr.add(&n);
int* pN = vctOfIntPtr[0];
std::cout << *pN;
}

Own vector error

I am trying create my own vector, I am at the beginning, and when compile e execute the code, i get "Program not responding". This is the code:
struct X
{
X(){};
~X(){};
int v1, v2, v3;
};
template<typename T>
class Vector
{
public:
// constructors
Vector();
Vector(unsigned s);
virtual ~Vector();
// overloaded operators
T operator[](unsigned index);
// others
void clear();
void add(T value);
unsigned getSize();
bool isEmpty();
private:
// pointer to first item of memory block
T* first;
unsigned size;
};
template<typename T>
Vector<T>::Vector()
{
first = NULL;
size = 0;
}
template<typename T>
Vector<T>::Vector(unsigned s)
{
size = s;
first = new T[s];
};
template<typename T>
Vector<T>::~Vector()
{
clear();
}
template<typename T>
void Vector<T>::clear()
{
for(unsigned i = size ; i > 0 ; i--)
delete &first[i];
first = NULL;
}
template<typename T>
void Vector<T>::add(T value)
{
T* temp = new T[size + 1]; // error happens here
// copy data to new location
for(unsigned i = 0 ; i < size ; i++)
temp[i] = first[i];
// delete older data
clear();
// add the new value in last index
temp[size + 1] = value;
// update the pointer
first = temp;
size++;
}
template<typename T>
T Vector<T>::operator[](unsigned index)
{
return first[index];
}
template<typename T>
unsigned Vector<T>::getSize()
{
return size;
}
template<typename T>
bool Vector<T>::isEmpty()
{
return first == NULL;
}
int main(int argc, char* args[])
{
Vector<X> anything;
X thing;
anything.add(thing);
anything.add(thing);
anything.add(thing); // if remove this line, program work fine.
}
As I commented, error happens in T* temp = new T[size + 1];.
If i define the value of v1, v2, v3 of X class, e.g. X() : v1(0), v2(0), v3(0) { }, the program works correctly.
If i change the type, e.g., Vector of int, he works perfectly.
If put X class in std::vector, work fine too.
Other comments are also accepted.
Can someone helpme?
Your description of the problem is incredibly vague, but I can point out problems with your code:
No vector copy constructor (causes double-deletes and crashes)
No vector copy assignment (causes double-deletes and crashes)
clear is incorrectly calling delete (causes crashes and corruption) (you should match your single new of an array with a single delete of the array. Don't loop over elements.
add is writing past the end of the array (causes crashes and corruption)
add is not exception safe
You have to fix at least the first four. The third and fourth are probably the causes of your hang.
You have a buffer overflow occurring.
T* temp = new T[size + 1]; // When size is 0, you allocate 1 space.
You then assign to the temp array, but in location temp[1], which isn't a valid location because your array has only 1 element. This is undefined behavior, and that this point, your program is free to continue however it chooses. In this case, it seems to loop indefinitely.
// add the new value in last index
temp[size + 1] = value; // When size is zero, your array is length '1', but
// you are accessing temp[1] which is outside the
// bounds of your allocated memory.

Help with implementing a function to change size of dynamic array

I'm trying to write a function that will change the size of a dynamic array to a new size. In my header file, I have:
Image **images; //pointer to a dynamic array of image pointers
int maximum; //size
I want to do this by allocating a new array and copying the values over without changing their indices. If there are non-null pointers outside the range newmax, then we cant do this. So heres what I have:
There are no compilation or runtime errors. However, I find that the new array isnt getting sized right. When I run the following test case:
I should get an index out of bounds error, but instead the system lets it slide. Can anyone see the mistake? I've looked for hours but cant find anything.
images=newArray;
for (int i =0;i<newmax;i++)
*images[i]=*newArray[i];
This is odd. images and newArray are now the same, hence no need to copy the contents of newArray back to itself. So remove this loop? Also, need to add:
maximum = newmax;
If the '1' is the index, this should cause
firstScene->addpicture("red10.bmp", 1, 13, 72);
to give an out of bounds error, whereas at the moment it might seg fault?
You should extract this functionality to a separate class and have a data member in Scene of that type:
struct ImagePtrVector {
typedef Image *value_type;
typedef int size_type;
ImagePtrVector() : _begin (), _end (), _end_alloc () {}
~ImagePtrVector() {
for (value_type x = _begin; x != _end; ++x) {
delete *x;
}
delete[] _begin;
}
// Either define these two yourself or mark private:
ImagePtrVector(ImagePtrVector const &x);
ImagePtrVector& operator=(ImagePtrVector const &x);
value_type& operator[](size_type index) {
assert(0 <= index); // Or other checking as you like.
assert(index < size());
return _begin[index];
}
value_type const& operator[](size_type index) const {
assert(0 <= index); // Or other checking as you like.
assert(index < size());
return _begin[index];
}
size_type size() const { return _end - _begin; }
size_type capacity() const { return _end_alloc - _begin; }
void reserve(size_type capacity) {
if (this->capacity() < capacity) {
value_type *new_begin = new value_type[capacity];
// Exception-safe only because I know the below won't throw.
std::copy(_begin, _end, new_begin);
_end_alloc = new_begin + capacity;
_end = new_begin + this->size();
delete[] _begin;
_begin = new_begin;
}
}
void resize(size_type size) {
reserve(size);
for (size_type diff = size - this->size(); diff > 0; --diff) {
*_end++ = new Image();
}
}
// Add push_back, begin/end, etc. to taste.
private:
value_type *_begin, *_end, *_end_alloc;
};
The differences from std::vector and boost::ptr_vector are not coincidence, and you should evaluate whether you really need to write a special container or can reuse an existing generic container.