I'm trying to recreate the inbuilt vector class in cpp to get a bit more practice with classes and memory management. I keep getting an error that says 'ISO C++ forbids declaration of ‘allocator’ with no type' and I can't figure out why for the life of me. Is there something I'm missing?
#include <cstddef>
#include <memory>
template <class T>
class myvector{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
myvector(){ data = avail = limit = 0; }
explicit myvector(size_type n, const T& t = T()) { create(n,t); }
myvector(const myvector& v){ create(v.begin(), v.end()); }
~myvector() { uncreate(); }
myvector& operator=(const myvector& v)
{
if (&v != this){
uncreate();
create(v.begin(), v.end());
}
return *this;
}
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
iterator begin(){ return data; }
const_iterator begin() const{ return data; }
iterator end(){ return limit; }
const_iterator end() const{ return limit; }
size_type size(){ return avail - data; }
void push_back(T t)
{
if(avail == limit)
size_type new_size = max(2*(limit-data),ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = uninitialized_copy(data,avail,new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
alloc.construct(avail++,t);
}
private:
iterator data;
iterator avail;
iterator limit;
allocator<T> alloc;
void create(size_type n, const T& t)
{
data = alloc.allocate(n);
limit = avail = data+n;
uninitialized_fill(data,limit,t);
}
void create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j-i);
limit = avail = uninitialized_copy(i,j,data);
}
void uncreate()
{
if(data){
iterator it = avail;
while(it != data) { alloc.destroy(--it); }
alloc.deallocate(data,limit-data);
}
data = limit = avail = 0;
}
};
It should be, std::allocator<T> alloc;, everything in the standard library is contained within the namespace std.
Alos see http://code.google.com/p/owasp-esapi-cplusplus/source/browse/trunk/esapi/util/zAllocator.h. It includes work arounds for GCC bugs.
Note: the standard C++ containers only have to check for overflow on vector::resize (hopefully that has changed by now). The ESAPI allocator checks during all allocations.
Related
I wrote my own Matrix class with such fields (you can't use STL containers in it)
template <typename T>
class Matrix
{
private:
T *data = nullptr;
size_t rows;
size_t cols;
I also made an iterator for this class:
public:
class Iterator
{
friend Matrix;
private:
T *curr;
public:
Iterator() : curr(nullptr) {}
Iterator(Matrix *matrix) : curr(matrix->data) {}
~Iterator() = default;
......
Iterator begin()
{
Iterator it(this);
std::cout << *it.curr << std::endl;
return it;
}
Iterator end()
{
Iterator it(this);
/*Iterator it(this->data + rows * cols);
if (it == nullptr)
{
return it;
}*/
return it;
}
How can I properly write an end() method that would return the next element after the last one? I understand that using address arithmetic you can get the latter, but how can I do it in my code?
I suggest that you let the iterator take a T* as argument. You can then supply data to the begin() iterator and data + rows * cols to the end() iterator.
Example:
template <class T>
class Matrix {
private:
T* data = nullptr;
size_t rows;
size_t cols;
public:
Matrix(size_t Rows, size_t Cols)
: data{new T[Rows * Cols]}, rows{Rows}, cols{Cols} {}
Matrix(const Matrix&) = delete;
Matrix(Matrix&&) = delete;
~Matrix() { delete[] data; }
class iterator {
private:
T* curr; // store a T*
public:
iterator(T* t) : curr{t} {} // take a T* and store it
iterator& operator++() { ++curr; return *this; }
bool operator==(const iterator& rhs) const { return curr == rhs.curr; }
bool operator!=(const iterator& rhs) const { return !(curr == rhs.curr); }
T& operator*() { return *curr; }
T* operator->() { return curr; }
};
iterator begin() { return {data}; } // begin() takes data
iterator end() { return {data + rows * cols}; } // end() takes data + rows *cols
};
Demo
However, you don't need to implement an iterator yourself for this. You can simply use pointers:
template <class T>
class Matrix {
private:
T* data = nullptr;
size_t rows;
size_t cols;
public:
Matrix(size_t Rows, size_t Cols)
: data{new T[Rows * Cols]}, rows{Rows}, cols{Cols} {}
Matrix(const Matrix&) = delete;
Matrix(Matrix&&) = delete;
~Matrix() { delete[] data; }
using iterator = T*; // iterator is now simply a T*
iterator begin() { return data; }
iterator end() { return data + rows * cols; }
};
I wrote my own matrix class with fields like this
template <typename T>
class Matrix
{
private:
T *data = nullptr;
size_t rows;
size_t cols;
....
I tried to make an iterator for my matrix, but it didn't work. An error like this is thrown in method Iterator begin(and end):
error: cannot convert ‘myMatrix::Matrix<int>::Iterator’ to ‘int*’ in return
The iterator must support STL functions such as std::find_if(). How can I fix the iterator so that it works correctly?
class Iterator
{
friend Matrix;
private:
T *curr;
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = value_type &;
Iterator() : curr(nullptr) {}
Iterator(T *other) : curr(other) {}
~Iterator() = default;
bool operator==(const Iterator &it) const { return curr == it.curr; }
bool operator!=(const Iterator &it) const { return !(curr == it.curr); }
Iterator &operator++()
{
++curr;
return *this;
}
Iterator operator++(int)
{
Iterator temp = *this;
operator++();
return temp;
}
Iterator &operator+(int n)
{
for (int i = 0; i < n; i++)
{
++(*this);
}
return *this;
}
T &operator*() const { return *curr; }
T *operator->() const { return curr; }
};
Iterator begin()
{
Iterator it(data);
return it;
}
Iterator end()
{
Iterator it(data + rows * cols);
return it;
}
From my previous question: How to implement erase on vector in c++, I have tried to implement erase via allocating new memory, and copying all elements except of that one erase to new array. From the answers, it is not a standard way of vector from STL, where the vector keeps old elements untouched, and only moves elements back after the one erased (this is part of the "invalidation" of previous iterators. So I have tried to use std::move():
#ifndef _vec_h
#define _vec_h
#include <stddef.h>
#include <memory>
#include <algorithm>
template<class T>
class Vec
{
public:
typedef T* iterator;
typedef T* const const_iterator;
typedef T value_type;
Vec() {
create();
}
explicit Vec(size_t n, const T& val = T()) {
create(n, val);
}
Vec(const Vec& v) {
create(v.begin(), v.end());
}
Vec& operator=(const Vec& v);
~Vec() {
uncreate();
}
T& operator[](size_t i) {
return data[i];
}
const T& operator[](size_t i) const {
return data[i];
}
void push_back(const T& val);
size_t size() const {
return avail - data;
}
iterator begin() {
return data;
}
const_iterator begin() const {
return data;
}
iterator end() {
return avail;
}
const_iterator end() const {
return avail;
}
void clear() {
if (data)
{
for (iterator i=avail; i!=data; i--)
i->~T();
}
avail=data;
}
std::allocator<T> alloc;
private:
iterator data;
iterator avail;
iterator limit;
void create();
void create(size_t n, const T& val);
void create(const_iterator b, const_iterator e);
void uncreate();
void grow();
void unchecked_append(const T& val);
};
template<class T> Vec<T>& Vec<T>::operator=(const Vec& rhs)
{
if (&rhs != this)
{
uncreate();
create(rhs.begin(), rhs.end());
}
return *this;
}
template<class T> void Vec<T>::push_back(const T& val)
{
if (avail == limit)
{
grow();
}
unchecked_append(val);
}
template<class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
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);
}
template<class T> void Vec<T>::create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j-i);
limit = avail = std::uninitialized_copy(i, j, data);
}
template<class T> void Vec<T>::uncreate()
{
if (data)
{
iterator i = avail;
while (i!=data)
{
alloc.destroy(--i);
}
alloc.deallocate(data, limit-data);
}
data=limit=avail=0;
}
template<class T> void Vec<T>::grow()
{
size_t new_size = std::max(2*(limit-data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template<class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, 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())
{
std::move(i+1, avail, i); //shift elements to the left
return i;
}
else
{
return 0;
}
}
#endif
Which still causes segfault. So is there any way, how to implement the erase?
To compile I needed to add this method declaration:
T* erase(T *const i);
I added some quick test code to recreate your segfault:
int main()
{
Vec<int> test;
test.push_back(1);
test.push_back(2);
test.push_back(3);
test.push_back(4);
test.erase(test.begin() + 2);
std::cout << test[2] << std::endl; // should output 4
}
However, the code outputed 4 as expected, and I did not get any segfault. I tried running in valgrind, and I didn't get any memory errors either. It appears to be working as you intended, but if you add some code to recreate the segfault, I will update the answer with a fix.
I am working on implementing a vector class but cannot figure out how to write a function to copy one vector into another.
template <class T> class Vec {
public:
//TYPEDEFS
typedef T* iterator;
typedef const T* const_iterator;
typedef unsigned int size_type;
//CONSTRUCTOS, ASSIGNMENT OPERATOR, & DESTRUCTOR
Vec() {this->create(); }
Vec(size_type n, const T& t = T()) { this->create(n, t); }
Vec(const Vec& v) { copy(v); }
Vec& operator=(const Vec& v);
~Vec() { delete [] m_data; }
//MEMBER FUNCTIONS AND OTHER OPERATORS
T& operator[] (size_type i) { return m_data[i]; }
const T& operator[] (size_type i) const { return m_data[i]; }
void push_back (const T& t);
iterator erase(iterator p);
void resize(size_type n, const T& fill_in_value = T());
void clear() { delete [] m_data; create(); }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
//ITERATOR OPERATIONS
iterator begin() { return m_data; }
const_iterator begin() const { return m_data; }
iterator end() { return m_data + m_size; }
const_iterator end() const { return m_data + m_size; }
private:
//PRIVATE MEMBER FUNCTIONS
void create();
void create(size_type n, const T& val);
void copy(const Vec<T>& v);
//REPRESENTATION
T *m_data; //point to first location inthe allocated array
size_type m_size; //number of elements stored in the vector
size_type m_alloc; //number of array locations allocated, m_size <= m_alloc
};
//create an empty vector (null pointers everywhere)
template <class T> void Vec<T>::create() {
m_data = NULL;
m_size = m_alloc = 0; //no memory allocated yet
}
//create a vector with size n, each location having the given value
template <class T> void Vec<T>::create(size_type n, const T& val) {
m_data = new T[n];
m_size = m_alloc = n;
for (T* p = m_data; p != m_data + m_size; ++p)
*p = val;
}
//assign one vector to another, avoiding duplicate copying
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v) {
if (this != &v) {
delete [] m_data;
this -> copy(v);
}
return *this;
}
This is the first things I came up with:
template <class T> void Vec<T>::copy(const Vec<T>& v) {
m_size = m_alloc = v.size();
m_data = &v;
}
I got an error about incompatible types...OK, makes sense that they are incompatible. So I take out the 'const' and now it works.
template <class T> void Vec<T>::copy(Vec<T>& v) {
m_size = m_alloc = v.size();
m_data = &v[0];
}
I am guessing this isn't entirely correct or good form. I'm not sure. And now I get an error about the pointer being freed not having been allocated (but it compiles, runs, and copies the vector successfully at least now). So I would say I am not really understanding passing variables/arrays/vectors/things by reference, and also dynamic allocation of memory. My question is: how can I improve the copy function that I have written to either not compare two incompatible variables, or to successfully allocate the pointer dynamically to the new vector so that I don't get that malloc error?
You need to do deep-copy of the elements, not simply assign the pointer m_data:
// precondition: `m_data` is not allocated
template <class T> void Vec<T>::copy(const Vec<T>& v) {
m_data = new T[v.size()];
m_size = m_alloc = v.size();
for (size_t ii = 0; ii < m_size; ++ii)
m_data[ii] = v[ii];
}
For your copy-constructor to be safe it needs to fail when the copy can't be made.
Vec<T>::Vec(const Vec<T>& o):m_size(o.m_size), m_alloc(o.m_size), m_data(new T()){
std::copy( o.m_data, o.m_data+o.m_size, m_data);
}
The copy-constructor should be able to replace any Vec<T>::copy member.
The assignment is easily handled by introducing a swap function. This is exception safe.
void Vec<T>::swap(Vec<T>& rhs){
std::swap(m_data, rhs.m_data);
std::swap(m_size, rhs.m_size);
std::swap(m_capacity, rhs.m_capacity);
}
With the exception safe Copy & swap & idiom it becomes:
Vec<T>& Vec<T>::operator = (const Vec<T>& rhs){
Vec<T> cpy=rhs;
swap( this, cpy);
return *this;
}
Note that there is an issue with exception safety with the previous answer given. The simple fix is to allocate first.
// precondition: `m_data` is not allocated
template <class T> void Vec<T>::copy(const Vec<T>& v) {
m_data = new T[v.size()];
m_size = m_alloc = v.size();
for (size_t ii = 0; ii < m_size; ++ii)
m_data[ii] = v[ii];
}
The other issue with your code is operator=, which is not exception safe. You deleted m_data before allocating again with new[]. If new[] fails, you've corrupted your object.
Once you have the fix to copy as above, then operator = can be written in terms of the copy constructor:
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v)
{
// construct a temporary
Vec<T> temp = v;
// set the members
m_size = m_alloc = temp.size();
delete [] m_data;
m_data = temp.m_data;
// Now set temp.m_data to 0, so that destruction of temp doesn't delete
// our memory
temp.m_data = 0;
return *this;
}
Basically, we construct a temporary from v, delete the this->m_data and then assign the elements of temp to this. Then we remove the guts of temp by setting the temp.m_data data to NULL. This needs to be done so that when temp dies, we don't want to delete the data we assigned to this.
Note that if the first line Vec<T> temp = v; throws an exception, no harm to this is
done, thus exception safety is provided.
Here is the copy/swap idiom, as suggested by Captain Giraffe:
template <class T> class Vec {
//...
void swap(Vec<T>& left, Vec<T>& right);
//..
};
template <class T> void Vec<T>::swap(Vec<T>& left, Vec<T>& right)
{
std::swap(left.m_size, right.m_size);
std::swap(left.m_alloc, right.m_alloc);
std::swap(left.m_data, right.m_data);
}
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v)
{
// construct a temporary
Vec<T> temp = v;
swap(*this, temp);
return *this;
}
The difference here is that we're swapping the members of temp with this. Since temp will now contain the pointer that this used to have, when temp dies, it will be calling delete on this "old" data.
I was making a copy function here, got a lot of good answers, now moving on to a resize function. The Vec class I'm working on looks like this:
template <class T> class Vec {
public:
//TYPEDEFS
typedef T* iterator;
typedef const T* const_iterator;
typedef unsigned int size_type;
//CONSTRUCTOS, ASSIGNMENT OPERATOR, & DESTRUCTOR
Vec() {this->create(); }
Vec(size_type n, const T& t = T()) { this->create(n, t); }
Vec(const Vec& v) { copy(v); }
Vec& operator=(const Vec& v);
~Vec() { delete [] m_data; }
//MEMBER FUNCTIONS AND OTHER OPERATORS
T& operator[] (size_type i) { return m_data[i]; }
const T& operator[] (size_type i) const { return m_data[i]; }
void push_back (const T& t);
void swap(Vec<T>& left, Vec<T>& right);
iterator erase(iterator p);
void resize(size_type n, const T& fill_in_value = T());
void clear() { delete [] m_data; create(); }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
//ITERATOR OPERATIONS
iterator begin() { return m_data; }
const_iterator begin() const { return m_data; }
iterator end() { return m_data + m_size; }
const_iterator end() const { return m_data + m_size; }
private:
//PRIVATE MEMBER FUNCTIONS
void create();
void create(size_type n, const T& val);
void copy(const Vec<T>& v);
//REPRESENTATION
T *m_data; //point to first location inthe allocated array
size_type m_size; //number of elements stored in the vector
size_type m_alloc; //number of array locations allocated, m_size <= m_alloc
};
//create an empty vector (null pointers everywhere)
template <class T> void Vec<T>::create() {
m_data = NULL;
m_size = m_alloc = 0; //no memory allocated yet
}
//create a vector with size n, each location having the given value
template <class T> void Vec<T>::create(size_type n, const T& val) {
m_data = new T[n];
m_size = m_alloc = n;
for (T* p = m_data; p != m_data + m_size; ++p)
*p = val;
}
//assign one vector to another, avoiding duplicate copying
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v) {
Vec<T> temp = v;
swap(*this, temp);
return *this;
}
//swap one vector with another
template <class T> void Vec<T>::swap(Vec<T>& left, Vec<T>& right) {
std::swap(left.m_size, right.m_size);
std::swap(left.m_alloc, right.m_alloc);
std::swap(left.m_data, right.m_data);
}
This is what the resize function looks like so far:
template <class T> void Vec<T>::resize(size_type n, const T& fill_in_value) {
if (n<=m_size) { //if resize is smaller than original size, just resize
m_size = n;
}
else { //if resize is bigger, assign more space at the end and fill with a value
size_type temp = m_size;
m_size = n;
//for (T* p = m_data; p != m_data + temp; ++p) {
// std::cout << p << std::endl;
//}
//std::cout << std::endl;
for (T* p = &m_data[0] + temp; p != m_data + m_size; ++p) {
// std::cout << p << std::endl;
*p = fill_in_value;
}
}
}
When I use this in a program, I get a segmentation fault. I'm guessing there is something wrong with:
*p = fill_in_value
So I print out the values of p and it seems to give the appropriate number of spaces for whatever I put in (8 bits for a double, etc), so from the beginning of the vector there are exactly the number of spaces to resize and they all have the right number of bits. What is it that I am not understanding here?
EDIT: If I change it so that:
m_size = m_alloc = n;
does that mean that I am allocating new memory for the rest of the data? Or should I create a temporary vector with the new size and replace the existing vector with that? I don't really understand how to correct the issue that I am not truly resizing, but just accessing out of bounds. The way I am understanding it so far is that I change the size, add stuff to the memory addresses at the end, but leave the pointer to the first value the same (the name of the vector). This is not working, so I'm wondering what my conceptual misunderstanding is.
EDIT2: I've made this correction to the code based on the answers here and it works:
template <class T> void Vec<T>::resize(size_type n, const T& fill_in_value) {
if (n<=m_size) {
m_size = m_alloc = n;
}
else {
T* new_data = new T[n];
for (size_type i = 0; i < m_size; i++) {
new_data[i] = m_data[i];
}
for (size_type i = m_size; i < n; i++) {
new_data[i] = fill_in_value;
}
delete m_data;
m_data = new_data;
m_size = m_alloc = n;
}
}
You haven't actually allocated space for new elements. When new size is bigger than current, you should allocate new storage and copy existing elements there.
T* new_data = new T[n];
for ( size_type i = 0; i < m_size; ++i )
{
new_data[i] = m_data[i];
}
for ( size_type i = m_size; i < n; ++i )
{
new_data[i] = fill_in_value;
}
delete m_data;
m_data = new_data;
m_size = n;