I am new to C++ world and I need a help. My problem is I try implement my structure hash pair array, there is key and data. In this structure I have nested structure iterator with methods hasNext and next. Because I can not see my array (this array is in parent) from nested structure I need pass it through constructor, but there is error ": cannot convert from...", problem is with pass _array in method getIterator. Code is below. Could you help me? Thanks
#pragma once
template<typename T, typename U, int Size, int(*HashFunction)(T)>
struct HashPairPole {
// Pair - key - data
struct Par {
// key
T _first;
// data
U _second;
// list for collision records
Par* _overflow;
Par(T t, U u) {
_first = t;
_second = u;
_overflow = nullptr;
}
};
HashParovePole() {}
// Static array for save data
Par* _array[Size];
// Add record into hash table
void add(T t, U u) {
// calculating of index
Par* prvek;
int idx = HashFunction(t) % Size;
// Element will be saved in _array[idx], if it is free, else will be
//saved to list (->_overflow)
prvek = new Par(t, u);
if (_array[idx] == nullptr) {
_array[idx] = prvek;
}
else {
prvek->_overflow = _array[idx];
}
_array[idx] = prvek;
}
// Get data from hash tabule
U& get(T t) {
int idx = HashFunction(t) % Size;
Par * prvni = _array[idx];
while (prvni->_overflow != nullptr) {
if (prvni->_first == t) {
return prvni->_second;
}
prvni = prvni->_overflow;
}
}
U& operator[](T t) {
return get(t);
}
U operator[](T t) const {
const U temp = get(t);
return temp;
}
// Iterator for walking all hash table
struct iterator {
Par* index[Size];
Par* pomPar;
int temp = 0;
iterator(Par * _array) {
index = _array;
pomPar = index[0];
}
bool hasNext()const {
return pomPar != nullptr;
}
std::pair<T, U> next() {
std::pair<T, U> data;
if (hasNext()) {
data.first = pomPar->_first;
data.second = pomPar->_second;
pomPar = pomPar->_overflow;
}
temp++;
pomPar = index[temp];
return data;
}
};
// Vytvori iterator
iterator getIterator() {
return iterator(_array);
}
};
As far as I see, the problem is in this line:
Par* _array[Size];
Here you declare an array of size Size of pointers to Par structures, which is probably not what you want.
Later you try to pass this array to constructor iterator(Par * _array), which accepts a pointer to Par structure, which is impossible.
I would fix this code in the following way:
Par _array[Size]; // Instead of Par* _array[Size]
// You need an array of structures instead of array of pointers
...
Par* index; // Instead of Par* index[Size]
// Here looks like index is a pointer to a current element
...
pomPar = index; // Instead of pomPar = index[0];
// This is a pointer to the node, while index[0] is its value
Also, consider using std::vector instead of raw pointers. It will handle memory management issues for you.
Related
I'm looking for an implementation of a stack allocated 2d array (array of arrays) which supports O(1) reads. You can guess what I mean from the below picture. Black are filled entries, white are possible gaps from erasure.
The implementation should allow fast random access O(1) on each element but also allow insertion and erase operations which do not shift around too many elements (there might be string objects located there).
Information for each array is held in an object like this:
struct array
{
iterator begin_;
iterator end_;
array* next_;
array* prev_;
};
It contains information on where this particular array starts and the memory neighbours (prev_ and next_).
I'm looking for a proven battle hardened algorithm for insertion and erasing that I can rely on. I tried constructing a few on my own but they tend to become very complicated very quickly.
Hurdles:
When arrays are shifted, each updated array needs to somehow receive the memo (adapt begin and end pointers).
Array objects will be themselves located in an array. This means that with every additional data member of struct array, the memory requirements of the whole thing will grow by member_size * 2d_array_size.
I'm open for all suggestions!
I am thinking of an idea, where we can segment the storage into different segments of size n. entire buffer size will be the multiple of n.
When a new array is to be initialized, we allocate a segment to it. normal array operations can be performed there. when it needs more space, it request for one more segment, and if more segment space available we allocate it to them to extend it.
In this case, Minimum length of an array cannot go below segment size n. this size n can be fine tuned as per requirement for better space efficiency and utilization.
Each segment is numbered, so we can calculate the index of an element and fetch it in O(1).
Sample program (In Python):
class segment:
def __init__(self,number):
self.number=number
class storage:
def __init__(self):
self.size=100
self.default_value=None
self.array=[self.default_value]*self.size
self.segment_size=5
self.number_of_segments =len(self.array)//self.segment_size
self.segment_map=[None]*self.number_of_segments
def store(self,index,value):
if index<self.size and index>=0:
self.array[index]=value
def get(self,index):
return self.array[index]
def get_next_segment(self):
new_seg=None
for i,seg in enumerate(self.segment_map):
if seg == self.default_value:
new_seg= segment(i)
break
self.occupy_segment(new_seg)
self.clean_segment(new_seg)
return new_seg
def occupy_segment(self,seg):
self.segment_map[seg.number]=True
def free_segment(self,seg):
self.segment_map[seg.number]=self.default_value
def destroy_segment(self,seg):
self.clean_segment(seg)
self.free_segment(seg)
def clean_segment(self,segment):
if segment==None:
return
segment_start_index=((segment.number) * (self.segment_size)) + 0
segment_end_index=((segment.number) * (self.segment_size)) + self.segment_size
for i in range(segment_start_index,segment_end_index):
self.store(i,self.default_value)
class array:
def __init__(self,storage):
self.storage=storage
self.length=0
self.segments=[]
self.add_new_segment()
def add_new_segment(self):
new_segment=self.storage.get_next_segment()
if new_segment==None:
raise Exception("Out of storage")
self.segments.append(new_segment)
def is_full(self):
return self.length!=0 and self.length%self.storage.segment_size==0
def calculate_storage_index_of(self,index):
segment_number=index//self.storage.segment_size
element_position=index%self.storage.segment_size
return self.segments[segment_number].number * self.storage.segment_size + element_position
def add(self,value):
if self.is_full():
self.add_new_segment()
last_segement=self.segments[-1]
element_position=0
if self.length!=0:
element_position=(self.length%self.storage.segment_size)
index=(last_segement.number*self.storage.segment_size)+element_position
self.__store(index,value)
self.length+=1
def __store(self,index,value):
self.storage.store(index,value)
def update(self,index,value):
self.__store(
self.calculate_storage_index_of(index),
value
)
def get(self,index):
return self.storage.get(self.calculate_storage_index_of(index))
def destroy(self):
for seg in self.segments:
self.storage.destroy_segment(seg)
st=storage()
array1=array(st)
array1.add(3)
Hi I did not have enough time to implement a full solution (and no time to complete it) but here is the direction I was taking (live demo : https://onlinegdb.com/sp7spV_Ui)
#include <cassert>
#include <array>
#include <stdexcept>
#include <vector>
#include <iostream>
namespace meta_array
{
template<typename type_t, std::size_t N> class meta_array_t;
// template internal class not for public use.
namespace details
{
// a block contains the meta information on a subarray within the meta_array
template<typename type_t, std::size_t N>
class meta_array_block_t
{
public:
// the iterator within a block is of the same type as that of the containing array
using iterator_t = typename std::array<type_t, N>::iterator;
/// <summary>
///
/// </summary>
/// <param name="parent">parent, this link is needed if blocks need to move within the parent array</param>
/// <param name="begin">begin iterator of the block</param>
/// <param name="end">end iterator of the block (one past last)</param>
/// <param name="size">cached size (to not have to calculate it from iterator differences)</param>
meta_array_block_t(meta_array_t<type_t, N>& parent, const iterator_t& begin, const iterator_t& end, std::size_t size) :
m_parent{ parent },
m_begin{ begin },
m_end{ end },
m_size{ size }
{
}
// the begin and end methods allow a block to be used in a range based for loop
iterator_t begin() const noexcept
{
return m_begin;
}
iterator_t end() const noexcept
{
return m_end;
}
// operation to shrink the size of the last free block in the meta-array
void move_begin(std::size_t n) noexcept
{
assert(n <= m_size);
m_size -= n;
m_begin += n;
}
// operation to move a block n items back in the meta array
void move_to_back(std::size_t n) noexcept
{
m_begin += n;
m_end += n;
}
std::size_t size() const noexcept
{
return m_size;
}
// assign a new array to the sub array
// if the new array is bigger then the array that is already there
// then move the blocks after it toward the end of the meta-array
template<std::size_t M>
meta_array_block_t& operator=(const type_t(&values)[M])
{
// move all other sub-arrays back if the new sub-array is bigger
// if it is smaller then adjusting the end iterator of the block is fine
if (M > m_size)
{
m_parent.move_back(m_end, M - m_size);
}
m_size = M;
// copy will do the right thing (copy from back to front) if needed
std::copy(std::begin(values), std::end(values), m_begin);
m_end = m_begin + m_size;
return *this;
}
private:
meta_array_t<type_t, N>& m_parent;
std::size_t m_index;
iterator_t m_begin;
iterator_t m_end;
std::size_t m_size;
};
} // details
//---------------------------------------------------------------------------------------------------------------------
//
template<typename type_t, std::size_t N>
class meta_array_t final
{
public:
meta_array_t() :
m_free_size{ N },
m_size{ 0ul },
m_last_free_block{ *this, m_buffer.begin(), m_buffer.end(), N }
{
}
~meta_array_t() = default;
// meta_array is non copyable & non moveable
meta_array_t(const meta_array_t&) = delete;
meta_array_t operator=(const meta_array_t&) = delete;
meta_array_t(meta_array_t&&) = delete;
meta_array_t operator=(meta_array_t&&) = delete;
// return the number of subarrays
std::size_t array_count() const noexcept
{
return m_size;
}
// return number of items that can still be allocated
std::size_t free_size() const noexcept
{
return m_free_size;
}
template<std::size_t M>
std::size_t push_back(const type_t(&values)[M])
{
auto block = allocate(M);
std::copy(std::begin(values), std::end(values), block.begin());
return m_blocks.size();
}
auto begin()
{
return m_blocks.begin();
}
auto end()
{
return m_blocks.end();
}
auto& operator[](const std::size_t index)
{
assert(index < m_size);
return m_blocks[index];
}
private:
friend class details::meta_array_block_t<type_t, N>;
void move_back(std::array<type_t,N>::iterator begin, std::size_t offset)
{
std::copy(begin, m_buffer.end() - offset - 1, begin + offset);
// update block administation
for (auto& block : m_blocks)
{
if (block.begin() >= begin )
{
block.move_to_back(offset);
}
}
}
auto allocate(std::size_t size)
{
if ((size == 0ul) || (size > m_free_size)) throw std::bad_alloc();
if (m_last_free_block.size() < size)
{
compact();
}
m_blocks.push_back({ *this, m_last_free_block.begin(), m_last_free_block.begin() + size, size });
m_last_free_block.move_begin(size);
m_free_size -= size;
m_size++;
return m_blocks.back();
}
void compact()
{
assert(false); // not implemented yet
// todo when a gap is found between 2 sub-arrays (compare begin/end iterators) then move
// the next array to the front
// the array after that will move to the front by the sum of the gaps ... etc...
}
std::array<type_t, N> m_buffer;
std::vector<details::meta_array_block_t<type_t,N>> m_blocks;
details::meta_array_block_t<type_t,N> m_last_free_block;
std::size_t m_size;
std::size_t m_free_size;
};
} // meta_array
//---------------------------------------------------------------------------------------------------------------------
#define ASSERT_TRUE(x) assert(x);
#define ASSERT_FALSE(x) assert(!x);
#define ASSERT_EQ(x,y) assert(x==y);
static constexpr std::size_t test_buffer_size = 16;
template<typename type_t, std::size_t N>
void show_arrays(meta_array::meta_array_t<type_t, N>& meta_array)
{
std::cout << "\n--- meta_array ---\n";
for (const auto& sub_array : meta_array)
{
std::cout << "sub array = ";
auto comma = false;
for (const auto& value : sub_array)
{
if (comma) std::cout << ", ";
std::cout << value;
comma = true;
}
std::cout << "\n";
}
}
void test_construction()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
ASSERT_EQ(meta_array.array_count(),0ul);
ASSERT_EQ(meta_array.free_size(),test_buffer_size);
}
void test_push_back_success()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
ASSERT_EQ(meta_array.array_count(),3ul); // cont
ASSERT_EQ(meta_array.free_size(),(test_buffer_size-9ul));
}
void test_range_based_for()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
show_arrays(meta_array);
}
void test_assignment()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 4,5,6 });
meta_array.push_back({ 7,8,9 });
meta_array[0] = { 11,12 }; // replace with a smaller array then what there was
meta_array[1] = { 21,22,23,24 }; // replace with a bigger array then there was
show_arrays(meta_array);
}
//---------------------------------------------------------------------------------------------------------------------
int main()
{
test_construction();
test_push_back_success();
test_range_based_for();
test_assignment();
return 0;
}
I'm trying to understand how the iterator works.
I have build the following code:
list<Bucket<Key,Val>> *array;
template<typename Key, typename Val>
class Bucket
{
public:
Bucket(Key key, Val value)
{
this->key = key;
this->value = value;
}
~Bucket(){}
const Key getKey() const
{
return key;
}
const Val getValue() const
{
return value;
}
private:
Key key;
Val value;
};
After inserting a bucket in one of the lists, I would like to do 2 actions at least:
Find and Remove.
I can look for a given value with this code:
int entryNum = HashFunc()(key);
for(auto i: array[entryNum % capacity])
{
if(MatchFunc(i.getKey(),key)())
{
value = i.getValue();
}
}
But I can't seem to delete an item by adding something like
i = array[entryNum % capacity].erase(i);
or with remove.
But if i do this, it works:
int entryNum = HashFunc()(key);
typename list<Bucket<Key,Val>>::iterator it;
for(auto i: array[entryNum % capacity])
{
++testcounter;
if(MatchFunc(i.getKey(),key)())
{
value = i.getValue();
}
}
for (it=array[entryNum % capacity].begin(); it!= array[entryNum % capacity].end(); ++it)
{
if(testcounter!=0) --testcounter;
else
{
array[entryNum % capacity].erase(it);
return htKeyFound;
}
}
This is obviously not ideal, I would like to NOT go twice over the same list + keeping a counter for it. But I can't seem to join them into one.
When I try to access the it.getKey() it obviously doesn't work and crests a compilation error.
Any advice about what I'm doing wrong?
Range for loops iterates over the values inside the container.
The std::list::erase function expects an iterator and not a value.
And you don't need two loops, you can dereference an iterator to get the value it "points" to:
int entryNum = HashFunc()(key);
auto& list_ref = array[entryNum % capacity];
for(auto it = list_ref.begin(); it != list_ref.end(); ++it)
{
if(MatchFunc(it->getKey(),key)())
{
value = it->getValue();
list_ref.erase(it);
return htKeyFound;
}
}
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;
}
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I want to be able to create a compare function using a class, eg:
bool someClassLess(const someClass &a1, const someClass &a2) {
return (a1.variable1 < a2.variable1 && a1.variable2 < a2.variable2);
}
Then declare a priority queue of someClass and pass my compare function to be used when pushing elements, eg:
PriorityQueue<someClass> arr(someClassLess());
If no compare function is passed when declaring the priority queue, it should automatically use the less function in functional library when pushing.
How do I pass a compare function to a class?
Below you can find code of my selfwritten PriorityQueue. The code includes a failed attempt to pass a compare function.
#ifndef PRIORITY_QUEUE_H
#define PRIORITY_QUEUE_H
#include <iostream>
#include <functional>
using std::cout;
using std::endl;
using std::less;
template<typename T>
class PriorityQueue {
public:
template<typename PRED>
PriorityQueue(PRED compare);
~PriorityQueue();
T pop();
void push(const T &e);
size_t getSize() const;
bool isEmpty() const;
void print() const;
private:
T *end;
T *queue;
PRED compare;
};
template<typename T>
PriorityQueue<T>::PriorityQueue(PRED compare = less<T>()) : queue(0), end(0), compare(compare) {
}
template<typename T>
PriorityQueue<T>::~PriorityQueue() {
delete [] queue;
}
template<typename T>
T PriorityQueue<T>::pop() {
if(isEmpty()) {
throw "Queue is empty";
} else if(getSize() == 1) {
T removed = *queue;
delete [] queue;
queue = end = 0;
return removed;
}
T *newQueue = new T[getSize() - 1];
// Iteratorer
T *it = queue, *itNew = newQueue;
T removed = *(it++);
for( ; it != end; it++, itNew++) {
*itNew = *it;
}
int oldSize = getSize();
T *tmp = queue;
queue = newQueue;
delete [] tmp;
end = queue + oldSize - 1;
return removed;
}
template<typename T>
void PriorityQueue<T>::push(const T &e) {
if (isEmpty()) {
queue = new T[1];
*queue = e;
end = queue + 1;
return;
}
T *newQueue = new T[getSize() + 1];
// Iterators
T *it = queue, *itNew = newQueue;
// Find where element e should be inserted, whilst inserting elements
// compare(*it, e) used to look like *it < e when I was initially creating the class
for( ; compare(*it, e) && it != end; it++, itNew++) {
*itNew = *it;
}
// Insert e
*(itNew++) = e;
// Insert the remaining elements
for ( ; it != end; it++, itNew++) {
*itNew = *it;
}
int oldSize = getSize();
T *tmp = queue;
queue = newQueue;
delete [] tmp;
end = queue + oldSize + 1;
}
template<typename T>
size_t PriorityQueue<T>::getSize() const {
return (end - queue);
}
template<typename T>
bool PriorityQueue<T>::isEmpty() const {
return (getSize() <= 0);
}
template<typename T>
void PriorityQueue<T>::print() const {
for(int *i = queue; i != end; i++) {
cout << *i << endl;
}
}
#endif
Why not just shamelessly copy the implementation of std::priority_queue. Just use this:
template <typename T, typename Compare = std::less< T > >
class PriorityQueue {
public:
PriorityQueue(Compare comp = Compare()) : end(0), queue(0), compare(comp) { };
// ...
private:
T *end;
T *queue;
Compare compare;
};
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! :-)