I have this implementation of vector that I've been working on for a few days using examples from a textbook:
#include <iostream>
#include <string>
#include <cassert>
#include <algorithm>
#include <cstring>
// Vector.h
using namespace std;
template <class T>
class Vector
{
public:
typedef T * iterator;
Vector();
Vector(unsigned int size);
Vector(unsigned int size, const T & initial);
Vector(const Vector<T> & v); // copy constructor
~Vector();
unsigned int capacity() const; // return capacity of vector (in elements)
unsigned int size() const; // return the number of elements in the vector
bool empty() const;
iterator begin(); // return an iterator pointing to the first element
iterator end(); // return an iterator pointing to one past the last element
T & front(); // return a reference to the first element
T & back(); // return a reference to the last element
void push_back(const T & value); // add a new element
void pop_back(); // remove the last element
void reserve(unsigned int capacity); // adjust capacity
void resize(unsigned int size); // adjust size
void erase(unsigned int size); // deletes an element from the vector
T & operator[](unsigned int index); // return reference to numbered element
Vector<T> & operator=(const Vector<T> &);
private:
unsigned int my_size;
unsigned int my_capacity;
T * buffer;
};
template<class T>//
Vector<T>::Vector()
{
my_capacity = 0;
my_size = 0;
buffer = 0;
}
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T[my_size];
for (int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
}
template<class T>//
Vector<T>::Vector(unsigned int size)
{
my_capacity = size;
my_size = size;
buffer = new T[size];
}
template<class T>//
Vector<T>::Vector(unsigned int size, const T & initial)
{
my_size = size; //added = size
my_capacity = size;
buffer = new T [size];
for (int i = 0; i < size; i++)
buffer[i] = initial;
}
template<class T>//
Vector<T> & Vector<T>::operator = (const Vector<T> & v)
{
delete[ ] buffer;
my_size = v.my_size;
my_capacity = v.my_capacity;
buffer = new T [my_size];
for (int i = 0; i < my_size; i++)
buffer[i] = v.buffer[i];
return *this;
}
template<class T>//
typename Vector<T>::iterator Vector<T>::begin()
{
return buffer;
}
template<class T>//
typename Vector<T>::iterator Vector<T>::end()
{
return buffer + size();
}
template<class T>//
T& Vector<T>::Vector<T>::front()
{
return buffer[0];
}
template<class T>//
T& Vector<T>::Vector<T>::back()
{
return buffer[size - 1];
}
template<class T>
void Vector<T>::push_back(const T & v)
{
if (my_size >= my_capacity)
reserve(my_capacity +5);
buffer [my_size++] = v;
}
template<class T>//
void Vector<T>::pop_back()
{
my_size--;
}
template<class T>//
void Vector<T>::reserve(unsigned int capacity)
{
if(buffer == 0)
{
my_size = 0;
my_capacity = 0;
}
if (capacity <= my_capacity)
return;
T * new_buffer = new T [capacity];
assert(new_buffer);
copy (buffer, buffer + my_size, new_buffer);
my_capacity = capacity;
delete[] buffer;
buffer = new_buffer;
}
template<class T>//
unsigned int Vector<T>::size()const
{
return my_size;
}
template<class T>//
void Vector<T>::resize(unsigned int size)
{
reserve(size);
my_size = size;
}
template<class T>//
T& Vector<T>::operator[](unsigned int index)
{
return buffer[index];
}
template<class T>//
unsigned int Vector<T>::capacity()const
{
return my_capacity;
}
template<class T>//
Vector<T>::~Vector()
{
delete[]buffer;
}
template<class T>
void Vector<T>::erase(unsigned int size)
{
}
int main()
{
Vector<int> v;
v.reserve(2);
assert(v.capacity() == 2);
Vector<string> v1(2);
assert(v1.capacity() == 2);
assert(v1.size() == 2);
assert(v1[0] == "");
assert(v1[1] == "");
v1[0] = "hi";
assert(v1[0] == "hi");
Vector<int> v2(2, 7);
assert(v2[1] == 7);
Vector<int> v10(v2);
assert(v10[1] == 7);
Vector<string> v3(2, "hello");
assert(v3.size() == 2);
assert(v3.capacity() == 2);
assert(v3[0] == "hello");
assert(v3[1] == "hello");
v3.resize(1);
assert(v3.size() == 1);
assert(v3[0] == "hello");
Vector<string> v4 = v3;
assert(v4.size() == 1);
assert(v4[0] == v3[0]);
v3[0] = "test";
assert(v4[0] != v3[0]);
assert(v4[0] == "hello");
v3.pop_back();
assert(v3.size() == 0);
Vector<int> v5(7, 9);
Vector<int>::iterator it = v5.begin();
while (it != v5.end())
{
assert(*it == 9);
++it;
}
Vector<int> v6;
v6.push_back(100);
assert(v6.size() == 1);
assert(v6[0] == 100);
v6.push_back(101);
assert(v6.size() == 2);
assert(v6[0] == 100);
v6.push_back(101);
cout << "SUCCESS\n";
}
So far it works pretty well, but I want to add a couple of functions to it that I can't find examples for, a SWAP function that would look at two elements of the vector and switch their values and and an ERASE function that would delete a specific value or range of values in the vector. How should I begin implementing the two extra functions?
I would use this as an exercise to see how the iterator design pattern works.
vector does not have a swap because this operation can be done in a more generic way with iterators. The std::swap algorithm does this for you,
see here
Similarly for the erase, you might want to use std::transform algorithm - http://www.cplusplus.com/reference/algorithm/transform/ - depending exactly what you mean by erase (do you mean delete or overwrite?)
Implementing swap should be very easy. Make a copy of buffer[A], assign buffer[B] to buffer[A], assign the copy to buffer[B].
Erase should also be fairly straight forward. Given a range of elements to erase, shift the elements after the range to the left by the size of the range, then resize the vector.
You don't need to define your own swap function, as the <iostream> header includes the function std::swap. But, if you don't want to use the std::swap, you can define your own swap function just like this:
template<typename _Tp>
void swap(_Tp &a, _Tp &b)
{
_Tp tempVal = a;
a = b;
b = tempVal;
}
Now about the erase function: because you are implementing the vector with an array, your erase function should:
1)call the destructor of the element you want to erase(if it has)
2)move all the elements which where at the right of the deleted element one position to the left
3)return a random access iterator pointing to the new location of the element that followed the last element erased by the function call, which is the vector end if the operation erased the last element in the sequence.
4)resize the vector
Suppose that you have the erase function, which erases one element, then the version of erase which erases all the elements devoted by the iterators a, b would look like that:
iterator
erase(iterator first, iterator last)
{
while (first != last)
first = erase(first);
return last;
}
Swapping two elements is easy, because std::swap will do that for you.
Erasing means that you will have to copy elements following the erased one(s) to fill the "hole". Then subtract from size.
BTW, you have a problem where you copy and assign from another vector. You copy the other vector's capacity, but allocate size elements in the buffer. This will come back and bite you later! :-)
Related
I want to construct a class of an array whose size is not known,
i want to make objects of array whose size would be initialized at the time when i would create an object(runtime)
class array_class{
public:
int size_of_array;
int arr[size_of_array];
array_class(int p_size_of_array){ //constructor
this->size_of_array=p_size_of_array;
}
};
it say's error of invalid use of non static data member, what wrong am i doing(what should i have known)?.
You can't have something like
void foo(int n)
{
int vals[n];
}
in C++.
The thing is called variable-length array and it's supported in C99.
By the way, it is the only thing I know which is supported in C99 and not supported in C++17 :).
std::vector is a nice alternative.
If you know the size of an array at the compile time, you may use std::array.
I want to construct a class of an array whose size is not known
(what should i have known)?
You should have known that is not not possible in C++. The size of all classes is compile time constant.
i want to make objects of array whose size would be initialized at the time when i would create an object(runtime)
You need to allocate the array dynamically.
A simple solution:
struct array_class {
std::vector<int> arr;
array_class(int p_size_of_array) : arr(p_size_of_array) {}
};
#eerorika and #Nestor have good answers. To add to them here is a simple implementation of a vector i did some time ago you can take as a reference.
As you will see a basic way to have a variable length data structure, is to delete the underlying array and create a larger one if more or less memory is needed.
#pragma once
#include <ostream>
#include <string>
#include <initializer_list>
#include <stdexcept>
static constexpr size_t min_sz = 5;
template<typename T>
class Vector
{
public:
class ConstIterator;
class Iterator;
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = Vector::Iterator;
using const_iterator = Vector::ConstIterator;
/// constructors, destructor, assign
Vector()
{
allocEmpty(min_sz);
}
Vector(Vector<value_type> &vec)
{
allocEmpty(vec.max_sz);
for (size_type i = 0; i < vec.sz; ++i)
values[i] = vec.values[i];
sz = vec.sz;
}
Vector(size_type size)
{
allocEmpty(size);
}
Vector(const std::initializer_list<value_type> &list)
{
allocEmpty(list.size());
size_type i = 0;
for (const auto &val : list)
{
values[i] = val;
++i;
}
sz = list.size();
}
~Vector()
{
delete[] values;
}
Vector &operator=(const Vector &other)
{
reserve(other.sz);
for (size_type i = 0; i < other.sz; ++i)
values[i] = other.values[i];
sz = other.sz;
return *this;
}
/// element access
reference operator[](size_type position)
{
if (position >= sz)
throw std::out_of_range ("operator[] out of range: sz = " + std::to_string(sz) + " but position = " + std::to_string(position));
return values[position];
}
const_reference operator[](size_type position) const
{
if (position >= sz)
throw std::out_of_range ("operator[] out of range: sz = " + std::to_string(sz) + " but position = " + std::to_string(position));
return values[position];
}
const_reference front() const
{
if (sz == 0)
throw std::out_of_range ("front called on empty container");
return values[0];
}
const_reference back() const
{
if (sz == 0)
throw std::out_of_range ("back called on empty container");
return values[sz - 1];
}
pointer data()
{
if (sz > 0)
return values;
return nullptr;
}
const_pointer data() const
{
if (sz > 0)
return values;
return nullptr;
}
/// capacity
size_type size() const
{
return sz;
}
size_type capacity() const
{
return max_sz;
}
bool empty() const
{
return (sz == 0);
}
void reserve(size_type size)
{
if (size <= min_sz)
return;
value_type *newArray = new value_type[size];
for (size_type i = 0; i < sz; ++i)
newArray[i] = values[i];
delete[] values;
values = newArray;
max_sz = size;
}
void shrink_to_fit()
{
size_type newSize = (sz >= min_sz) ? sz : min_sz;
value_type *newArray = new value_type[newSize];
for (size_type i = 0; i < sz; ++i)
newArray[i] = values[i];
delete[] values;
values = newArray;
max_sz = newSize;
}
/// modifiers
void push_back(const value_type &value)
{
if (sz >= max_sz)
reserve(max_sz * 2);
values[sz] = value;
++sz;
}
void resize(size_type count)
{
reserve(count);
sz = count;
}
void resize(size_type count, const value_type &value)
{
reserve(count);
for (size_type i = sz; i < count; ++i)
values[i] = value;
sz = count;
}
void insert(size_type position, const value_type &value)
{
if (position > sz)
throw std::out_of_range ("insert out of range: sz = " + std::to_string(sz) + " but position = " + std::to_string(position));
if (sz >= max_sz)
reserve(max_sz * 2);
for (size_type i = sz; i > position; --i)
values[i] = values[i - 1];
values[position] = value;
++sz;
}
iterator insert(const_iterator pos, const_reference val)
{
auto diff = pos - begin();
if (diff < 0 || static_cast<size_type>(diff) > sz)
throw std::runtime_error("Iterator out of bounds");
size_type current = static_cast<size_type>(diff);
if (sz >= max_sz)
reserve(max_sz * 2);
for (size_t i = sz; i-->current;)
values[i + 1] = values[i];
values[current] = val;
++sz;
return iterator(values + current);
}
void erase(size_type position)
{
if (position >= sz)
throw std::out_of_range ("erase out of range: sz = " + std::to_string(sz) + " but position = " + std::to_string(position));
for (size_type i = position; i < sz - 1; ++i)
values[i] = values[i + 1];
--sz;
}
iterator erase(const_iterator pos)
{
auto diff = pos - begin();
if (diff < 0 || static_cast<size_type>(diff) >= sz)
throw std::runtime_error("Iterator out of bounds");
size_type current = static_cast<size_type>(diff);
for (size_type i = current; i < sz - 1; ++i)
values[i] = values[i + 1];
--sz;
return iterator(values + current);
}
void pop_back()
{
if (sz == 0)
throw std::out_of_range ("pop_back on empty container");
if (sz > 0) --sz;
}
void clear()
{
sz = 0;
}
/// iterators
iterator begin()
{
if (sz == 0)
return end();
return iterator(values);
}
iterator end()
{
return iterator(values + sz);
}
const_iterator begin() const
{
if (sz == 0)
return end();
return ConstIterator(values);
}
const_iterator end() const
{
return ConstIterator(values + sz);
}
/// private section
private:
void allocEmpty(size_type size)
{
auto newSize = (size > min_sz) ? size : min_sz;
sz = 0;
values = new value_type[newSize];
max_sz = newSize;
}
private:
value_type *values;
size_type sz;
size_type max_sz;
/// iterator implementations
public:
class Iterator
{
public:
using value_type = Vector::value_type;
using reference = Vector::reference;
using pointer = Vector::pointer;
using difference_type = Vector::difference_type;
using iterator_category = std::forward_iterator_tag;
public:
Iterator()
{
ptr = nullptr;
}
Iterator(pointer ptr)
{
this->ptr = ptr;
}
reference operator*() const
{
return *ptr;
}
pointer operator->() const
{
return ptr;
}
iterator& operator++()
{
++ptr;
return *this;
}
iterator operator++(int)
{
iterator it = *this;
++ptr;
return it;
}
iterator operator+ (difference_type difference) const
{
return iterator(ptr + difference);
}
bool operator==(const const_iterator &it) const
{
return it == ptr;
}
bool operator!=(const const_iterator &it) const
{
return it != ptr;
}
operator const_iterator() const
{
return const_iterator(ptr);
}
private:
pointer ptr;
};
class ConstIterator
{
public:
using value_type = Vector::value_type;
using reference = Vector::const_reference;
using pointer = Vector::const_pointer;
using difference_type = Vector::difference_type;
using iterator_category = std::forward_iterator_tag;
public:
ConstIterator()
{
ptr = nullptr;
}
ConstIterator(pointer ptr)
{
this->ptr = ptr;
}
reference operator*() const
{
return *ptr;
}
pointer operator->() const
{
return ptr;
}
const_iterator& operator++()
{
++ptr;
return *this;
}
const_iterator operator++(int)
{
const_iterator it = *this;
++ptr;
return it;
}
bool operator==(const const_iterator &it) const
{
return it.ptr == ptr;
}
bool operator!=(const const_iterator &it) const
{
return it.ptr != ptr;
}
Vector::difference_type operator-(const const_iterator &rop)
{
return ptr - rop.ptr;
}
private:
pointer ptr;
};
};
/// non-member functions
template<typename T>
bool operator==(const Vector<T> &lop, const Vector<T> &rop)
{
if (lop.size() != rop.size()) return false;
for (size_t i = 0; i < lop.size(); ++i)
{
if (lop[i] != rop[i])
return false;
}
return true;
}
template<typename T>
std::ostream& operator<<(std::ostream &out, const Vector<T> &vec)
{
out << '[';
for (size_t i = 0; i < vec.size(); ++i)
{
if (i > 0) out << ", ";
out << vec[i];
}
out << ']';
return out;
}
So i have a custom made vector class with also an custom allocator:
#define _SCL_SECURE_NO_WARNINGS
#include <iostream>
#include <exception>
#include <sstream>
#include <string>
#include <iostream>
namespace mem {
template<typename T>
class allocator {
public:
//
T * allocate(int n); //allocate space for n objects of type T
void deallocate(T* p); //deallocate n objects of type T starting at p
void construct(T* p, const T& v); // construct a T with the value v in p
void destroy(T* p); // destroy the T in p
};
template<typename T> T* allocator<T>::allocate(int n) //allocate space for n objects of type T
{
T* res = (T*)malloc(sizeof(T)*n);
if (res == nullptr)
throw std::bad_alloc();
return res;
}
template<typename T> void allocator<T>::deallocate(T* p/*, int n*/) //deallocate n objects of type T starting at p
{
free(p);
}
template<typename T> void allocator<T>::construct(T* p, const T& v) // construct a T with the value v in p
{
new(p) T(v);
}
template<typename T> void allocator<T>::destroy(T* p) // destroy the T in p
{
p->~T();
}
}
namespace vec {
template<typename T, typename A = mem::allocator<T>>
class vector {
A alloc; //use allocate to handle memory for elements
int sz; //the size
T* elem; //a pointer to the elements
int space; //size + free space
public:
using size_type = unsigned long;
using value_type = T;
using iterator = T * ;
using const_iterator = const T*;
vector() :sz{ 0 }, elem{ nullptr }, space{ 0 } {}
explicit vector(int s) :sz{ s }, elem{ new T[s] }, space{ s }
{
for (int i = 0; i < sz; ++i) elem[i] = 0; // elements are initalized
}
vector(const vector& arg); //copy constructor
vector& operator =(const vector& a); //copy assignment
vector(vector&& a); //move constructor
vector& operator=(vector&& a); //move assignment
~vector() { delete[] elem; } //destructor
void reserve(int newalloc);
void resize(int newsize, T val = T()); //growth
void push_back(const T& val);
iterator begin() { return elem; }
const_iterator begin()const { return elem; }
iterator end() { return elem + sz; }
const_iterator end() const { return elem + sz; }
size_type size() { return sz; }
iterator back() { return end() - 1; }
const_iterator back() const { return end() - 1; }
};
template<typename T, typename A> vector<T, A>::vector(const vector<T, A>& a) //copy constructor
:sz{ a.sz }, elem{ new T[a.sz] }
{
copy(a.elem, a.elem + sz, elem);
}
template<typename T, typename A> vector<T, A>& vector<T, A>::operator =(const vector<T, A>& a) //copy assignment
{
if (this == &a) return *this; //return if self assignment
if (a.sz <= space) { //enough space no need for new allocation
for (int i = 0; i < a.sz; ++i) //copy elements
alloc.construct(&elem[i], a.elem[i]);
sz = a.sz;
return *this;
}
T* p = alloc.allocate(a.sz);//allocate new size
for (int i = 0; i < a.sz; ++i) //copy elements
alloc.construct(&p[i], a.elem[i]);
for (int i = 0; i < sz; ++i)
alloc.destroy(&elem[i]); //destroy
alloc.deallocate(elem);
space = sz = a.sz; //set new size
elem = p; //set new elements
return *this; //return a self reference
}
template<typename T, typename A> vector<T, A>::vector(vector<T, A>&& a) //move constructor
:sz{ a.sz }, elem{ a.elem }
{
a.sz = 0; //make a the empty vector
a.elem = nullptr;
}
template<typename T, typename A> vector<T, A>& vector<T, A>::operator=(vector<T, A>&& a) //move assignment
{
delete[] elem; //deallocate old space
elem = a.elem; //copy a's elem and sz
sz = a.sz;
a.elem = nullptr; //make a the empty vector
a.sz = 0;
return *this;
}
template<typename T, typename A> void vector<T, A>::reserve(int newalloc)
{
if (newalloc <= space) return; //never decrease allocation
T* p = alloc.allocate(newalloc); //alocate new space
for (int i = 0; i < sz; ++i)
alloc.construct(&p[i], elem[i]); //copy
for (int i = 0; i < sz; ++i)
alloc.destroy(&elem[i]); //destroy
alloc.deallocate(elem/*, space*/); //deallocate old space
elem = p;
space = newalloc;
}
template<typename T, typename A> void vector<T, A>::push_back(const T& val)
{
if (space == 0)
reserve(8);
else if (space == sz)
reserve(2 * space);
alloc.construct(&elem[sz], val); //add val at the end
++sz;
}
template<typename T, typename A> void vector<T, A>::resize(int newsize, T val)
{
reserve(newsize);
for (int i = sz; i < newsize; ++i)
alloc.construct(&elem[i], val); //construct
for (int i = newsize; i < sz; ++i)
alloc.destroy(&elem[i]); //destroy
sz = newsize;
}
template<typename T, typename A> std::ostream& operator<<(std::ostream& os, const vector<T, A>& obj) //requires Element<T> && Allocator<A>
{
for (const auto& x : obj)
os << x << ' ';
os << '\n';
return os;
}
template<typename T, typename A> std::istream& operator>>(std::istream& is, vector<T, A>& obj)
{
std::string line;
std::getline(is, line);
if (is.bad() || is.fail())
return is;
std::istringstream istr{ line };
vector<T, A> tmp;
while (true) {
T tmp_in = T();
istr >> tmp_in;
if (istr.fail()) {
istr.clear();
std::string invalid;
istr >> invalid;
continue;
}
tmp.push_back(tmp_in);
if (istr.eof())break;
}
for (const auto& x : tmp)
obj.push_back(x);
return is; //its sends me to xutility in this step
}
}
int main()
{
//vec::vector <int> test; //input "1 2 3 4 5" works fine with cin >>test
vec::vector <std::string> test; //input "this is a test" breaks into xutility
while (true) {
std::cin >> test; //breaks into xutility
std::cout << test << '\n';
}
}
Now the following problem. I can read in vector values if the vector is vector. So adding 1 2 3 4 5 is no problem.
Also vector works fine.
If i change the container type to vector and read in "hello this is a test" via cin >> test; I get a crahs in xutility.h:
// MEMBER FUNCTIONS FOR _Container_base12
inline void _Container_base12::_Orphan_all() _NOEXCEPT
{ // orphan all iterators
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Myproxy != 0) //It points on this line
{ // proxy allocated, drain it
_Lockit _Lock(_LOCK_DEBUG);
for (_Iterator_base12 **_Pnext = &_Myproxy->_Myfirstiter;
*_Pnext != 0; *_Pnext = (*_Pnext)->_Mynextiter)
(*_Pnext)->_Myproxy = 0;
_Myproxy->_Myfirstiter = 0;
}
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
}
Visual Studio 2017 throws:
Exception thrown: read access violation.this was 0xC83FB858.
How can this header even get called and what is the meaning of it?
I really have no idea whats going on.
I replaced the custom vector with std::vector and then it also works.
edit: I minimized the code a bit and added a namespace for the vector. still the same behavior:
vec::vector<int> test;
std::cin >> test; //Ok: Example input 1 2 3 4 5
vec::vector<std::string> test;
std::cin >> test; //Mem access violation in xutility: Example input "this is a test"
The application crashes because:
The code does not use allocator consistently and ends up doing free() on memory that was allocated with new[].
The code does not maintain vector::space variable consistently.
I'm trying to keep a vector of commands so that it keeps 10 most recent. I have a push_back and a pop_back, but how do I delete the oldest without shifting everything in a for loop? Is erase the only way to do this?
Use std::deque which is a vector-like container that's good at removal and insertion at both ends.
If you're amenable to using boost, I'd recommend looking at circular_buffer, which deals with this exact problem extremely efficiently (it avoids moving elements around unnecessarily, and instead just manipulates a couple of pointers):
// Create a circular buffer with a capacity for 3 integers.
boost::circular_buffer<int> cb(3);
// Insert threee elements into the buffer.
cb.push_back(1);
cb.push_back(2);
cb.push_back(3);
cb.push_back(4);
cb.push_back(5);
The last two ops simply overwrite the elements of the first two.
Write a wrapper around a vector to give yourself a circular buffer. Something like this:
include <vector>
/**
Circular vector wrapper
When the vector is full, old data is overwritten
*/
class cCircularVector
{
public:
// An iterator that points to the physical begining of the vector
typedef std::vector< short >::iterator iterator;
iterator begin() { return myVector.begin(); }
iterator end() { return myVector.end(); }
// The size ( capacity ) of the vector
int size() { return (int) myVector.size(); }
void clear() { myVector.clear(); next = 0; }
void resize( int s ) { myVector.resize( s ); }
// Constructor, specifying the capacity
cCircularVector( int capacity )
: next( 0 )
{
myVector.resize( capacity );
}
// Add new data, over-writing oldest if full
void push_back( short v )
{
myVector[ next] = v;
advance();
}
int getNext()
{
return next;
}
private:
std::vector< short > myVector;
int next;
void advance()
{
next++;
if( next == (int)myVector.size() )
next = 0;
}
};
What about something like this:
http://ideone.com/SLSNpc
Note: It's just a base, you still need to work a bit on it. The idea is that it's easy to use because it has it's own iterator, which will give you the output you want. As you can see the last value inserted is the one shown first, which I'm guessing is what you want.
#include <iostream>
#include <vector>
template<class T, size_t MaxSize>
class TopN
{
public:
void push_back(T v)
{
if (m_vector.size() < MaxSize)
m_vector.push_back(v);
else
m_vector[m_pos] = v;
if (++m_pos == MaxSize)
m_pos = 0;
}
class DummyIterator
{
public:
TopN &r; // a direct reference to our boss.
int p, m; // m: how many elements we can pull from vector, p: position of the cursor.
DummyIterator(TopN& t) : r(t), p(t.m_pos), m(t.m_vector.size()){}
operator bool() const { return (m > 0); }
T& operator *()
{
static T e = 0; // this could be removed
if (m <= 0) // if someone tries to extract data from an empty vector
return e; // instead of throwing an error, we return a dummy value
m--;
if (--p < 0)
p = MaxSize - 1;
return r.m_vector[p];
}
};
decltype(auto) begin() { return m_vector.begin(); }
decltype(auto) end() { return m_vector.end(); }
DummyIterator get_dummy_iterator()
{
return DummyIterator(*this);
}
private:
std::vector<T> m_vector;
int m_pos = 0;
};
template<typename T, size_t S>
void show(TopN<T,S>& t)
{
for (auto it = t.get_dummy_iterator(); it; )
std::cout << *it << '\t';
std::cout << std::endl;
};
int main(int argc, char* argv[])
{
TopN<int,10> top10;
for (int i = 1; i <= 10; i++)
top10.push_back(5 * i);
show(top10);
top10.push_back(60);
show(top10);
top10.push_back(65);
show(top10);
return 0;
}
I want to extract a sub-vector. Then modify its elements which affects the original vector. My sample code below:
#include <vector>
#include <iostream>
using namespace std;
void printvec(vector<int>& v){
for(int i = 0;i < v.size();i++) {cout << v[i] << " ";}
cout << endl;
}
int main(){
vector<int> original;
for(int i = 1;i <= 10;i++) original.push_back(i);
printvec(original);
vector<int> subvector(original.begin()+4, original.end()-2);
subvector[0]=0;
subvector[1]=0;
printvec(subvector);
printvec(original);
return 0;
}
In above code, subvector does not modify vector. Can some one point me to an elegant way to make a subvector which modifies original vector (hopefully without explicit use of pointers if possible).
If you don't want to use a pointer, you could create a slice class to forward the work to - which will just be a pair of iterators and whatever other operations you might need:
template <typename T>
class slice {
using iterator = typename T::iterator;
using reference = typename std::iterator_traits<iterator>::reference;
slice(iterator first, iterator last)
: first(first), last(last)
{ }
reference operator[](size_t idx)
{
return *std::next(first, idx);
}
iterator begin() const { return first; }
iterator end() const { return last; }
private:
iterator first, last;
};
With that, you can do your slicing thusly:
slice<vector<int>> subvector(original.begin()+4, original.end()-2);
subvector[0]=0; // changes original[4]
subvector[1]=0; // changes original[5]
If you change your printvec to take an arbitrary container and use a range-for to iterate over it, you can print the subvector too. It will contain:
0 0 7 8
The line:
vector<int> subvector(original.begin()+4, original.end()-2);
creates a new vector and copies the elements from original.begin()+4 to original.end()-2 to the new one. Even with pointers, there is no way I would call elegant to achieve what you want, because many change to the original vector (rezise / push_back) could potentially invalidate the pointers to its elements.
Depending on the exact functionality you want to implement, you can use a class like this:
#include <vector>
#include <iostream>
using namespace std;
template<class T>
class Subvector {
std::vector<T>*const vec;
size_t start;
size_t end;
public:
Subvector(std::vector<T>& vector, size_t start, size_t end) :
vec(&vector),
start(start),
end(end)
{}
size_t size() const { return end - start; }
T& operator[](size_t i) {
return (*vec)[start + i];
}
const T& operator[](size_t i) const {
return (*vec)[start + i];
}
};
template<class VEC>
void printvec(const VEC& v){
for (int i = 0; i < v.size(); i++) { cout << v[i] << " "; }
cout << endl;
}
int main(){
vector<int> original;
for (int i = 1; i <= 10; i++) original.push_back(i);
printvec(original);
Subvector<int> subvector(original,4, original.size() - 2);
subvector[0] = 0;
subvector[1] = 0;
printvec(subvector);
printvec(original);
return 0;
}
I'm writing an implementation of Deque as a programming exercise and it's not going too well at all. I'm missing a few key function that are needed to make the test main program I was given function correctly.
Here is my code so far:
#include <vector>
#include <iostream>
#include <cassert>
using namespace std;
template <class T> class DequeIterator;
template <class T>
class Deque {
public:
typedef DequeIterator<T> iterator;
Deque(): vecOne(), vecTwo() { }
Deque(unsigned int size, T& initial): vecOne(size/2, initial), vecTwo(size-(size/2), initial) { }
Deque(Deque<T> & d): vecOne(d.vecOne), vecTwo(d.vecTwo) { }
T & operator[](unsigned int);
T & front();//
T & back();//
bool empty(){ return vecOne.empty() && vecTwo.empty(); }
iterator begin() { return iterator(this,0); }
iterator end() { return iterator(this, size ()); }
void erase(const iterator &);
void erase(const iterator &, const iterator &);
void insert(const iterator &, const T &);
int size() { return vecOne.size() + vecTwo.size(); }
void push_front(const T & value) { vecOne.push_back(value); }
void push_back(const T & value) {vecTwo.push_back(value); }
void pop_front();
void pop_back();
protected:
vector<T> vecOne;
vector<T> vecTwo;
};
template <class T>//
T & Deque<T>::front()//returns the first element in the deque
{
if (vecOne.empty())
return vecTwo.front();
else
return vecOne.back();
}
template <class T>//
T & Deque<T>::back()//returns the last element in the deque
{
if (vecOne.empty())
return vecTwo.back();
else
return vecOne.front();
}
template <class T>//
T & Deque<T>::operator[] (unsigned int index)
{
int n = vecOne.size();
if (index < n)
return vecOne [ (n-1) - index ];
else
return vecTwo [ index - n ];
}
template <class T>//
Deque<T>::iterator DequeIterator<T>::operator ++ (int)
{
Deque<T>::iterator clone(theDeque, index);
index++;
return clone;
}
template <class T>//
void Deque<T>::pop_front()
{
}
template <class T>//
void Deque<T>::pop_back()
{
}
template <class T>//
void Deque<T>::erase (const iterator & itr)
{
int index = itr.index;
int n = vecOne.size();
if (index < n)
vecOne.erase (vecOne.begin() + ((n-1) - index));
else
vecTwo.erase (vecTwo.begin() + (n - index));
}
template <class T>//
void Deque<T>::erase (const iterator &, const iterator &)
{
}
template <class T>//
void Deque<T>::insert(const iterator &, const T &)
{
}
template <class T>
class DequeIterator {
friend class Deque<T>;
typedef DequeIterator<T> iterator;
public:
DequeIterator(): theDeque(0), index(0) { }
DequeIterator(Deque<T> * d, int i): theDeque(d), index(i) { }
DequeIterator(const iterator & d): theDeque(d.theDeque), index(d.index) { }
T & operator*() { return (*theDeque)[index]; }
iterator & operator++(int) { ++index; return *this; }
iterator operator++();
iterator operator--(int) { --index; return *this; }
iterator & operator--();
bool operator==(const iterator & r) { return theDeque == r.theDeque && index == r.index; }
bool operator!=(const iterator & r) { return theDeque == r.theDeque && index != r.index; }
bool operator< (const iterator & r) { return theDeque == r.theDeque && index < r.index; }
T & operator[](unsigned int i) { return (*theDeque) [index + i]; }
iterator operator=(const iterator & r) { theDeque = r.theDeque; index = r.index; }
iterator operator+(int i) { return iterator(theDeque, index + i); }
iterator operator-(int i) { return iterator(theDeque, index - i); }
protected:
Deque<T> * theDeque;
int index;
};
main()
{
Deque<int> d;
d.push_back(10);
d.push_back(20);
assert(d.front() == 10);
assert(d.back() == 20);
d.push_front(1);
d.push_front(2);
d.push_front(3);
assert(d.front() == 3);
assert(d.back() == 20);
d.pop_back();
d.pop_back();
d.pop_back();
assert(d.front() == 3);
assert(d.back() == 2);
d.push_back(1);
d.push_back(0);
Deque<int>::iterator i;
int counter = 3;
for (i = d.begin(); i != d.end(); i++)
assert(*i == counter--);
for (counter = 0; counter < d.size(); counter++)
assert(d[counter] == d.size()-counter-1);
i = d.begin() + 3;
Deque<int>::iterator j(i), k;
k = j = i - 2;
assert(*k == 2);
for (i = d.begin(); not(i == d.end()); ++i)
cout << *i << " ";
cout << endl;
d.erase(d.begin()+3);
//d.erase(d.begin(), d.begin()+2);
assert(d.size() == 1);
assert(d[0] == 1);
Deque<int> c(d);
c.front() = 3;
assert(c.back() == 3);
c.push_front(1);
c.insert(c.begin(), 0);
c.insert(c.begin()+2, 2);
for (i = c.begin(); not(i == c.end()); ++i)
cout << *i << " ";
cout << endl;
for (counter = 0; counter < c.size(); counter++)
assert(c[counter] == counter);
cout << "SUCCESS\n";
}
I was wondering if someone could tell me my function from line 66 is returning:
expected constructor, destructor, or type conversion before 'DequeIterator'
Because I'm not sure what I'm doing wrong in it. Also, if someone would be kind enough to give me an example of the pop_front() function so that I can use it to create the pop_back() function as well, that would be great. Lastly, I have on of the erase functions completed but I am not sure how to go about creating the second one, which basically erases a value within the range of two iterators, it is referenced in line 176.
Any help would be greatly appreciated. Thank you in advance.
As for the error, you probably need a typename before Deque<T>::iterator on that line.
typename Deque<T>::iterator DequeIterator<T>::operator++(int)
I think it is a great programming exercise to implement deque. But a prerequisite is to implement vector and list. deque is one of the most complicated std::containers to implement. You should start with one of the simpler ones (vector and list).
Well, you get the error on line 65 because you return an object of a class that hasn't been defined. You only have the forward declaration (prototype) for class DequeIterator, not the implementation.
void pop_back() {
vecTwo.pop_back();
}
void pop_front() {
vecOne.pop_back();
}