As the title says, I try to extend the std::vector class in that way if I erase an element, the value of the position is not erased but actually set to NULL (providing a gap).
template<typename T>
class FVector : public std::vector<T> {
typedef std::vector<T> Base;
protected:
/** The number of elements in the vector */
size_t elementCount;
/**
* The index of the last element. This field is actually vector's length.
* For example when you have a vector like this ["a","b","c"] then the
* elementCount would be 3 and lastIndex would be 2 (because indexes are
* zero-based). But if you erased two first elements,
* leaving [null, null, "c" ] then elementCount=1 (because there is only
* one element in the vector!) however lastIndex would still remain 2.
* After you erase "c" lastIndex would be set to -1 (so it's enough to
* add 1 to lastIndex to determine vector's length.
*/
int lastIndex;
private:
/**
* Returns the index of the last not-null element in the vector,
* starting from position position downwards.
*
* #param position the position from which counting is to be begun.
* #return last element before (or on) index <code>position</code>
*/
int FindLastIndex(int position) {
int nLastItem = position;
if (position < 0) {
return -1;
}
for (; nLastItem >= 0; nLastItem--) {
if (Base::operator[](nLastItem) != NULL) {
break;
}
}
return (nLastItem);
}
public:
FVector(const T & value = T())
: elementCount(0), lastIndex(-1) {
}
FVector(int initialCapacity, const T & value = T())
: elementCount(0), lastIndex(-1),
std::vector<T>(initialCapacity, value) {
}
size_t Capacity() const {
return Base::size();
}
size_t Size() const {
return elementCount;
}
int LastIndex() const {
return lastIndex;
}
void AddElement(const T& obj) {
Base::push_back(obj);
elementCount++;
lastIndex++;
}
T & ElementAt(int index) {
if (index > lastIndex) {
// error
}
return Base::at(index);
}
void EraseElementAt(int index) throw() {
if (index > lastIndex) {
std::stringstream ss;
ss << index << " > " << lastIndex;
throw ArrayIndexOutOfBoundsException(ss.str());
}
if (Base::operator[](index) != NULL) {
elementCount--;
T v = Base::at(index);
delete v;
Base::at(index) = NULL;
if (index == lastIndex) {
lastIndex = FindLastIndex(lastIndex - 1);
}
}
}
};
It is not working like I expect. When I call the erase() method on an element the element
is not set to NULL.
For example:
class Int {
int i;
public:
Int(int v): i(v) { };
~Int() { }
};
//...
FVector<Int *> * v = new FVector<Int *>();
v->AddElement(new Int(1));
v->AddElement(new Int(3));
v->AddElement(new Int(5));
v->EraseElementAt(0);
v->EraseElementAt(2);
// ...
delete v;
will result in
[null, 3]
but I expect it to be
[null, 3, null]
Well, I do not know if that is possible what I try to achieve. I thought taking the std::vector class, which is a dynamic array (so why should I write my own array class then) give me all the basics I need to implement such thing.
Can anybody shed some light on that, I think I have some implementation issues here.
Thanks for your help!
In your EraseElementAt you have this:
if (index == lastIndex) {
lastIndex = FindLastIndex(lastIndex - 1);
}
If you happen to erase the last element in the vector (which you did) it will shorten the vector (decrement lastIndex). It seems like you want your vector to not do this - rather you are wanting the vector to null but not shorten. Maybe just take this out.
Related
I have some doubts about my insert method. it is compiling, but with no result. I presume that it is containing some coding errors. Can you help me resolving this? Thanks in advance.
private:
T* elements;
int capacity;
int nbElements;
template <class T>
void TableDynamic<T>::insert(const T& element, int index)
{
int *temp = new int[capacity] ;
for(int i =0; i<nbElements; i++)
{
temp[i] = element;
}
delete[] elements;
int *elem = new int[capacite];
}
I have written some code for you. see if its works for you.
#include <iostream>
using namespace std;
// insert element using template
template <class T>
class TableDynamic
{
private:
T *elements;
int capacity;
int nbElements;
public:
TableDynamic(int capacity)
{
this->capacity = capacity;
this->nbElements = 0;
this->elements = new T[capacity];
}
void insert(const T &element, int index)
{
if (index < 0 || index > nbElements)
{
cout << "Index out of range" << endl;
return;
}
if (nbElements == capacity)
{
cout << "Table is full" << endl;
return;
}
for (int i = nbElements; i > index; i--)
{
elements[i] = elements[i - 1];
}
elements[index] = element;
nbElements++;
}
void print()
{
for (int i = 0; i < nbElements; i++)
{
cout << elements[i] << " ";
}
cout << endl;
}
};
int main()
{
TableDynamic<int> table(10);
table.insert(10, 0);
table.insert(20, 1);
table.insert(30, 2);
// print the table
table.print();
return 0;
}
temp and elem variables types should be T* , and in the last line you have wrote capacite, should be capacity
Resizing should only be done, if the capacity is insufficient.
Furthermore you didn't copy any of the old elements over; you simply fill every element with the new element.
Also you're generating an array of ints regardless of element type which will only allow for element types int or const int.
The code should look something like this:
template <class T>
void TableDynamic<T>::insert(const T& element, int index)
{
if ((index > nbElements) || (index < 0))
{
throw std::runtime_error("invalid index");
}
if ((nbElements + 1) > capacity)
{
// reallocate array
auto newCapacity = CalculateNewCapacity(capacity, nbElements + 1); // todo: implement capacity calulation function
auto temp = std::make_unique<T[]>(newCapacity);
temp[index] = element; // note copying may result in an error, moving shouldn't, so copy first
// move first half
std::move(elements, elements + index, temp.get());
// move second half
std::move(elements + index, elements + nbElements, temp.get() + (index + 1));
// replace array
delete[] elements;
elements = temp.release();
capacity = newCapacity;
}
else
{
// make room for new element
std::move_backward(elements + index, elements + nbElements, elements + (nbElements + 1));
// insert new element
elements[index] = element;
}
++nbElements;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Using this simple example of a binary heap. How would i implement this data structure using c++ code.
1
/ \
3 6
/\ /\
5 9 8
Also apart from being able to gain easy access to the max or min values in an array, how is this data structure useful?
the example come from the following link: http://www.algolist.net/Data_structures/Binary_heap
Here is my simplest C++ implementation for heap. The code is well-commented.
/*
Usage:
heap Heap;
Heap.clear();
Heap.insert(value);
Heap.remove();
Heap.print();
*/
struct heap {
int myarray[NN+1]; // myarray to store the numbers as heap, 1 indexed
int n; // the number of nodes in my array
heap() { // constructor
clear(); // we clear the heap
}
void clear() { // initialize the heap
n = 0; // initially there are no nodes in the heap
}
void insert( int K ) { // inserting an element K in the heap
if( n == NN ) { // the heap is full
printf("cannot insert any more element, the heap is full\n");
return;
}
++n; // so, we have a new element, we increased n before adding
// the element because we start from index 1
myarray[n] = K; // inserted the element at the rightmost position
int p = n; // for keeping the current position
while( p > 1 ) { // p = 1 means we are on the root, and its a heap
int pr = p / 2; // pr is the parent of p
if( myarray[pr] > myarray[p] ) { // parent is greater than child
swap( myarray[pr], myarray[p] );
p = pr; // now the new position of the current element is pr
} else break; // otherwise its a heap, so we can stop here
}
}
int remove() { // removing the minimum element from the heap
if( n == 0 ) { // is the heap is empty
printf("The heap is empty, cannot delete.\n");
return -1;
}
int K = myarray[1]; // first element in the heap is the minimum
myarray[1] = myarray[n]; // brought the last element in 1st position
n--; // as we removed one element, now we need to maintain the heap
int p = 1; // as we moved the rightmost element in index 1
while( 2 * p <= n ) { // means p has at least one child, if 2*p > n
// we are sure that p is in the last level
int ch = 2 * p; // contains the index of the child
if( 2 * p + 1 <= n ) { // right child exists
if( myarray[ch] > myarray[ch+1] ) // right child is smaller
// than left child
ch++; // ch contains the index of the right child
}
if( myarray[p] > myarray[ch] ) { // so, current node is larger
// than its child
swap( myarray[p], myarray[ch] );
p = ch; // new position of the current element
} else break; //current node is smaller than its children, so heap
}
return K; // as we stored the minimum element in K
}
void print() { // printing the heap
printf("Number of elements: %d\n", n);
for( int i = 1; i <= n; i++ ) printf("%d ", myarray[i]);
printf("\n");
}
// Time: O(nlogn)
// Extra space: O(1) as we will pass the input array as res here
void heapSort(int* res) {
for(int i = 0, len = n; i < len; ++i) {
res[i] = remove();
}
}
};
I am writing below Java implementation it can help you to write code in c++;
import java.util.Arrays;
/**
* Min heap implementation, also caters to duplicate
*/
public class MinHeap {`
private int capacity = 10;
private int size;
int[] items;
public MinHeap() {
items = new int[capacity];
size = 0;
}
public void ensureExtraCapacity() {
if (size == capacity) {
items = Arrays.copyOf(items, capacity * 2);
capacity *= 2;
}
}
private int getLeftChildIndex(int index) {
return 2 * index + 1;
}
private int getRightChildIndex(int index) {
return 2 * index + 2;
}
private int getParentIndex(int index) {
return (index - 1) / 2;
}
private boolean hasLeftChild(int index) {
return size > getLeftChildIndex(index);
}
private boolean hasRightChild(int index) {
return size > getRightChildIndex(index);
}
private boolean hasParent(int index) {
if(index == 0)
return false;
return getParentIndex(index) >= 0;
}
private int leftChild(int index) {
return items[getLeftChildIndex(index)];
}
private int rightChild(int index) {
return items[getRightChildIndex(index)];
}
private int parent(int index) {
return items[getParentIndex(index)];
}
private void swapValues(int index1, int index2) {
int temp = items[index1];
items[index1] = items[index2];
items[index2] = temp;
}
public int peek() {
if (size == 0) throw new IllegalStateException();
return items[0];
}
public int poll() {
if (size == 0) throw new IllegalStateException();
int polled = items[0];
items[0] = items[size - 1];
size--;
heapifyDown();
return polled;
}
public void add(int item) {
ensureExtraCapacity();
items[size] = item;
size++;
heapifyUp();
}
private void heapifyUp() {
int index = size - 1;
while (hasParent(index) && parent(index) > items[index]) {
swapValues(index, getParentIndex(index));
index = getParentIndex(index);
}
}
private void heapifyDown() {
int index = 0;
while (hasLeftChild(index)) {
int minimumChildIndex = getLeftChildIndex(index);
if (hasRightChild(index) && rightChild(index) < leftChild(index))
minimumChildIndex = getRightChildIndex(index);
if (items[index] < items[minimumChildIndex]) {
break;
} else {
swapValues(index, minimumChildIndex);
}
index = minimumChildIndex;
}
}
/* public void printMinHeap() {
while (size > 0) {
int poll = poll();
System.out.println(poll);
}
}*/
/* public static void main(String[] args) {
MinHeap minHeap = new MinHeap();
minHeap.add(7);
minHeap.add(3);
minHeap.add(4);
minHeap.add(10);
minHeap.add(1);
minHeap.add(15);
minHeap.add(2);
minHeap.add(17);
minHeap.add(1);
minHeap.printMinHeap();
}*/
}
I'm trying to store the numbers in my driver class by arranging them with insert and attach functions from the implementation file. Insert function is supposed to move the number to the left or a smaller position and attach moves to the right or higher position in the data array.This is the implementation one:
#include "Lab1A.h"
#include <iostream>
#include <cassert>
#include <algorithm>
sequence::sequence() {
used = 0;
current_index = 0;
}
// MUTATOR MEMBER FUNCTIONS
//Postcondition: The first item in the sequence becomes the current item
void sequence::start() {
current_index = 0;
//Precondition: is_item returns true.
//Postcondition: If the current item was already the last item in the
//sequence, then there is no longer any current item. Otherwise, the new
//current item is the item immediately after the original current item.
}
void sequence::advance() {
if (is_item()) {
current_index++;
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence
//before the current item. If there was no current item, then the new entry
//has been inserted at the front of the sequence (position 0). In either //case, the newly inserted item is now the current item of the sequence.
void sequence::insert(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used - 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: size( ) < CAPACITY.
//Postcondition: A new copy of entry has been inserted in the sequence //after the current item. If there was no current item, then the new entry //has been attached to the end of the sequence. In either case, the newly
//inserted item is now the current item of the sequence.
void sequence::attach(const value_type& entry) {
if (size() < CAPACITY) {
data[used] = data[used + 1];
data[used] = entry;
data[current_index] = entry;
used++;
}
if (is_item() == false) {
data[used] = entry;
data[used] = data[used + 1];
}
}
//Precondition: is_item returns true.
//Postcondition: The current item has been removed from the sequence, and //the item after this (if there is one) is now the new current item.
void sequence::remove_current() {
int i;
if (is_item()) {
current_index--;
data[i] = data[current_index];
}
}
// ACCESSOR MEMBER FUNCTIONS
//Postcondition: The value returned is the number of items in the
//sequence.
int sequence::size() const {
return used;
}
//Postcondition: A true return value indicates that there is a valid
//"current" item that may be retrieved by invoking the current
//member function below. A false return value indicates that
//there is no valid current item.
bool sequence::is_item() const {
return (current_index < used);
}
//Precondition: is_item( ) returns true.
//Postcondition: The item returned is the current item in the sequence.
sequence::value_type sequence::current() const {
return data[current_index];
}
void sequence::print() {
for (int j = 0; j < used; j++) {
cout << data[j] << " ";
}
}
Driver file:
#include <iostream>
#include <cstdlib>
#include "Lab1Aimplementation.cpp"
using namespace std;
int main()
{
sequence numbers;
numbers.insert(21);
numbers.attach(33);
numbers.insert(22);
numbers.print();
return 0;
}
I'm trying to get this output: 21 22 33
Instead I get: 22 33 22
Possible declaration of sequence as OP didn't attach one:
class sequence
{
using index_type = int;
using value_type = size_t;
static const index_type CAPACITY = 1024;
value_type data[CAPACITY];
index_type used;
index_type current_index;
public:
sequence();
void start();
void advance();
void insert(const value_type& entry);
void attach(const value_type& entry);
void remove_current();
int size() const;
bool is_item() const;
value_type current() const;
void print();
};
I'm using here these assumptions:
current_index can be represented by an iterator; when no element is available, it points to past-the-end.
insert is supposed to insert before the current item, attach is supposed to insert after the current item.
Both insertion methods, judging by the expected result (21 22 33) are such that after being called, the current item always refers to the newly inserted item.
This said, if you just wrap around a list, each function is basically a one liner, you just need to know that the list class does. Bonus: O(1) insertion and removal, capacity constraint virtually removed.
The OP resets current_index to 0, so it is reasonable to assume that it could point anywhere in the array. Therefore, it is not possible to achieve insertion just by swapping elements, you need to move a whole block of data. Try this out here.
#include <iostream>
#include <sstream>
#include <list>
#include <cassert>
template <typename T>
class sequence
{
private:
std::list<T> _l;
typename std::list<T>::iterator _i;
public:
using value_type = T;
using size_type = typename std::list<T>::size_type;
size_type size() const {
return _l.size();
}
T current() const {
assert(is_current_valid());
return *_i;
}
size_type current_index() const {
return _i - _l.begin();
}
void increase_current() {
if (is_current_valid()) {
++_i;
}
}
void decrease_current() {
if (_i != _l.begin()) {
--_i;
}
}
void reset_current() {
_i = _l.begin();
}
bool is_current_valid() const {
// "is_item"
return _i != _l.end();
}
void remove_current() {
assert(is_current_valid());
// _i takes the next current element, eventually end()
_i = _l.erase(_i);
}
void insert_before(const value_type &entry) {
// _i is always the newly inserted element
_i = _l.insert(_i, entry);
}
void insert_after(const value_type &entry) {
// _i is always the newly inserted element
assert(is_current_valid());
_i = _l.insert(++_i, entry);
}
friend std::ostream &operator<<(std::ostream &os, sequence const &s) {
for (auto it = s._l.begin(); it != s._l.end(); ++it) {
if (it != s._l.begin()) {
os << " " << *it;
} else {
os << *it;
}
}
return os;
}
sequence() : _l(), _i(_l.end()) {}
};
int main() {
sequence<std::size_t> numbers;
numbers.insert_before(21); // insert 21, then points to 21
numbers.insert_after(33); // 33 after 21, then points to 33
numbers.insert_before(22); // 22 before 21, then points to 22
std::cout << numbers << std::endl;
// Programmatically check if the result is the requested one
const std::string expected = "21 22 33";
std::stringstream output;
output << numbers;
if (output.str() != expected) {
std::cerr << "Error!" << std::endl;
return 1;
}
return 0;
}
Found the answer at http://www.cplusplus.com/forum/beginner/141458/. This code works, however I don't understand the logic behind what is I guess a backwards loop.
void sequence::attach(const value_type& entry) {// value_type is the declared typedef double data
int i;
if(!is_item()) // is_item checks if there's any number in the array
current_index = used - 1; // used keeps track of how many numbers are stored
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index+1] = entry;
++current_index;
++used;
}
void sequence::insert(const value_type& entry){
int i;
if(!is_item())
current_index = used;
for (i = used; i > current_index; --i)
data[i]=data[i-1];
data[current_index] = entry;
++current_index;
++used;
}
I am receiving the error "expression must have pointer-to-class type". I have searched the error and have not been able to find a post that I could figure out what is going on. I am receiving the error on Sequence2.cpp on line 49 (data->resize( capacity * 2 );)
Sequence2.h
typedef double value_type;
class sequence
{
private:
value_type* data;
int used;
int current_index;
int capacity;
public:
// TYPEDEFS and MEMBER CONSTANTS
static const int DEFAULT_CAPACITY = 5;
// CONSTRUCTORS and DESTRUCTOR
sequence(int initial_capacity = DEFAULT_CAPACITY);
sequence(const sequence& source);
~sequence();
// MODIFICATION MEMBER FUNCTIONS
void insert(const value_type& entry);
void resize(int new_capacity);
// ADDITIONAL FUNCTIONS THAT ARE NOT RELEVANT
Sequence2.cpp
#include "stdafx.h"
#include "Sequence2.h"
// Postcondition: The sequence has been initialized as an empty sequence.
// The insert/attach functions will work efficiently (without allocating
// new memory) until this capacity is reached.
sequence::sequence(int initial_capacity)
{
capacity = initial_capacity;
current_index = -1;
data = new value_type[DEFAULT_CAPACITY];
}
sequence::sequence(const sequence & source)
{
// NEED TO DO
// Postcondition: the sequence has made a deep copy of the source sequence.
}
sequence::~sequence()
{
delete[] data;
}
// Postcondition: A new copy of entry has been inserted in the sequence
// before the current item. If there was no current item, then the new entry
// has been inserted at the front of the sequence. In either case, the newly
// inserted item is now the current item of the sequence.
void sequence::insert(const value_type & entry)
{
if (current_index < 0)
{
current_index = 0; // Set current index to first index
data[current_index] = entry;
used++;
}
else
{
if (used < capacity) {
data[current_index + 1] = entry;
current_index++;
used++;
}
else
{
data->resize( capacity * 2 );
}
}
}
void sequence::resize(int new_capacity)
{
value_type *temp = new value_type[capacity];
int temp_capacity = capacity;
for (int i = 0; i < capacity; i++)
{
temp[i] = data[i];
}
delete[] data;
data = new value_type[new_capacity];
for (int i = 0; i < temp_capacity; i++)
{
data[i] = temp[i];
used++;
current_index++;
}
delete[] temp;
}
resize is a member function too, you're not calling it correctly. Change
data->resize( capacity * 2 );
to
resize( capacity * 2 );
Here're some other issues:
You might need to insert the value after calling resize() in insert().
No need to new/delete twice in resize().
The value of used and current_index seems wrong after resize().
I have a technical problem and it's really confusing me. I apologise in advance because I may not be giving the relevant details; I don't yet why it's going wrong and it would be excessive to include all the code I'm working with.
I'm working with a large program that uses the C++ STL. I'm moving this code to a very sensitive environment without a standard clib nor STL implementaton; it will redefine malloc/free/new/delete etc... For that, I need to replace the std:: parts with my own simplified implementations. I've started with std::vector. Right now it's running in the standard ecosystem so it's the GNU libc and STL. The only thing that's changed is this vector class.
When I execute the program with the replaced class, it segfaults. I've put this through GDB and found that the program will request an object from the vector using the subscript operator. When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class.
I'm really not sure at all what the problem is here. I would love to provide additional details, but I'm not sure what more I can give. I can only assume something is wrong with my vector implementation because nothing else in the program has been changed. Maybe there's something obvious that I'm doing wrong here that I'm not seeing at all.
I'm using: g++ (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6)
I'd really appreciate any feedback/advice!
#ifndef _MYSTL_VECTOR_H_
#define _MYSTL_VECTOR_H_
#include <stdlib.h>
#include <assert.h>
typedef unsigned int uint;
namespace mystl
{
/******************
VECTOR
********************/
template <typename T>
class vector
{
private:
uint _size;
uint _reserved;
T *storage;
void init_vector(uint reserve)
{
if (reserve == 0)
{
_reserved = 0;
return;
}
storage = (T*)malloc(sizeof(T)*reserve);
assert(storage);
_reserved = reserve;
}
public:
vector()
{
// std::cerr << "default constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
}
vector(const vector<T> &other)
{
// std::cerr << "copy constructor " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(other.size());
_size = other.size();
for (uint i=0; i<other.size(); i++)
{
storage[i] = T(other[i]);
}
}
vector(uint init_num, const T& init_value)
{
// std::cerr << "special constructor1 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
for (size_t i=0; i<init_num; i++)
{
push_back(init_value);
}
}
vector(uint init_num)
{
// std::cerr << "special constructor2 " << this << std::endl;
storage = NULL;
_size = 0;
_reserved = 0;
init_vector(init_num);
}
void reserve(uint new_size)
{
if (new_size > _reserved)
{
storage = (T*)realloc(storage, sizeof(T)*new_size);
assert(storage);
_reserved = new_size;
}
}
void push_back(const T &item)
{
if (_size >= _reserved)
{
if (_reserved == 0) _reserved=1;
reserve(_reserved*2);
}
storage[_size] = T(item);
_size++;
}
uint size() const
{
return _size;
}
~vector()
{
if (_reserved)
{
free(storage);
storage = NULL;
_reserved = 0;
_size = 0;
}
}
// this is for read only
const T& operator[] (unsigned i) const
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
T& operator[] (unsigned i)
{
// do bounds check...
if (i >= _size || i < 0)
{
assert(false);
}
return storage[i];
}
// overload = operator
const vector<T>& operator= (const vector<T>& x)
{
// check for self
if (this != &x)
{
_reserved = 0;
_size = 0;
storage = NULL;
init_vector( x.size() );
for(uint i=0; i<x.size(); i++)
{
storage[i] = T(x[i]);
}
_size = x.size();
}
return *this;
}
uint begin() const
{
return 0;
}
void insert(uint pos, const T& value)
{
push_back(value);
if (size() == 1)
{
return;
}
for (size_t i=size()-2; i>=pos&& i>=0 ; i--)
{
storage[i+1] = storage[i];
}
storage[pos] = value;
}
void erase(uint erase_index)
{
if (erase_index >= _size)
{
return;
}
//scoot everyone down by one
for (uint i=erase_index; i<_size; i++)
{
storage[i] = storage[i+1];
}
_size--;
}
void erase(uint start, uint end)
{
if (start > end)
{
assert(false);
}
if (end > _size)
end = _size;
for (uint i=start; i<end; i++)
{
erase(start);
}
assert(false);
}
void clear()
{
erase(0,_size);
}
bool empty() const
{
return _size == 0;
}
}; //class vector
}
#endif // _MYSTL_VECTOR_H_
Wow!
Your assignment operator also leaks memory.
Becuause you are using malloc/release the constructor to your type T will will not be called and thus you can not use your vector for anything except the most trivial of objects.
Edit:
I am bit bored this morning: Try this
#include <stdlib.h> // For NULL
#include <new> // Because you need placement new
// Because you are avoiding std::
// An implementation of swap
template<typename T>
void swap(T& lhs,T& rhs)
{
T tmp = lhs;
lhs = rhs;
rhs = tmp;
}
template <typename T>
class vector
{
private:
unsigned int dataSize;
unsigned int reserved;
T* data;
public:
~vector()
{
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Because we use placement new we must explicitly destroy all members.
data[loop].~T();
}
free(data);
}
vector()
: dataSize(0)
, reserved(10)
, data(NULL)
{
reserve(reserved);
}
vector(const vector<T> &other)
: dataSize(0)
, reserved(other.dataSize)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// Because we are using malloc/free
// We need to use placement new to add items to the data
// This way they are constructed in place
new (&data[loop]) T(other.data[loop]);
}
}
vector(unsigned int init_num)
: dataSize(0)
, reserved(init_num)
, data(NULL)
{
reserve(reserved);
dataSize = reserved;
for(unsigned int loop;loop < dataSize;++loop)
{
// See above
new (&data[loop]) T();
}
}
const vector<T>& operator= (vector<T> x)
{
// use copy and swap idiom.
// Note the pass by value to initiate the copy
swap(dataSize, x.dataSize);
swap(reserved, x.rserved);
swap(data, x.data);
return *this;
}
void reserve(unsigned int new_size)
{
if (new_size < reserved)
{ return;
}
T* newData = (T*)malloc(sizeof(T) * new_size);
if (!newData)
{ throw int(2);
}
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Use placement new to copy the data
new (&newData[loop]) T(data[loop]);
}
swap(data, newData);
reserved = new_size;
for(unsigned int loop = 0; loop < dataSize; ++loop)
{
// Call the destructor on old data before freeing the container.
// Remember we just did a swap.
newData[loop].~T();
}
free(newData);
}
void push_back(const T &item)
{
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Place the item in the container
new (&data[dataSize++]) T(item);
}
unsigned int size() const {return dataSize;}
bool empty() const {return dataSize == 0;}
// Operator[] should NOT check the value of i
// Add a method called at() that does check i
const T& operator[] (unsigned i) const {return data[i];}
T& operator[] (unsigned i) {return data[i];}
void insert(unsigned int pos, const T& value)
{
if (pos >= dataSize) { throw int(1);}
if (dataSize == reserved)
{
reserve(reserved * 2);
}
// Move the last item (which needs to be constructed correctly)
if (dataSize != 0)
{
new (&data[dataSize]) T(data[dataSize-1]);
}
for(unsigned int loop = dataSize - 1; loop > pos; --loop)
{
data[loop] = data[loop-1];
}
++dataSize;
// All items have been moved up.
// Put value in its place
data[pos] = value;
}
void clear() { erase(0, dataSize);}
void erase(unsigned int erase_index) { erase(erase_index,erase_index+1);}
void erase(unsigned int start, unsigned int end) /* end NOT inclusive so => [start, end) */
{
if (end > dataSize)
{ end = dataSize;
}
if (start > end)
{ start = end;
}
unsigned int dst = start;
unsigned int src = end;
for(;(src < dataSize) && (dst < end);++dst, ++src)
{
// Move Elements down;
data[dst] = data[src];
}
unsigned int count = start - end;
for(;count != 0; --count)
{
// Remove old Elements
--dataSize;
// Remember we need to manually call the destructor
data[dataSize].~T();
}
}
unsigned int begin() const {return 0;}
}; //class vector
With your current memory handling, this vector would only work with plain old data types.
To handle all types, it must ensure that objects
are actually created (malloc doesn't do that),
destroyed (free doesn't do that),
and you can't reallocate memory with realloc, because complex objects are not guaranteed to remain valid if they are byte-wise copied to another location.
Looks like the answer can be found in your question: "When the object reference is returned, a method is invoked and the program segfaults. It seems it can't find this method and ends up in main_arena() in GDB. The type of the object is an inherited class."
You probably store base class instance T in the vector, but make push_back for the instance of the class inherited from T. In push_back {storage[_size] = T(item);} you cast (actually make copy constructor T:T(const T&)) item to T (this probably named 'type cut'), then get reference to T and invoke a method of the class inherited from T using virtual table of T where the method is not defined yet/abstract. Am I right?
To make it properly work you should put T* in the vector or shared_ptr/unique_ptr depending on the ownership terms you apply to vector elements.
Generally in vector you can store only POD (Plain Old Data) types.