I am having trouble building a class that makes sure the user does not access an element that is off the end of the array, by building a class that mimics the behavior of an array, but adds a check. That is, this class will create a sequence of elements of a given type, and allow access to these elements with the [] bracket operator, but it will check to make sure that the user does not try to do something with an element that doesn't exist.
Here is the instructions on building it.
I have no idea how to make an index operator for this case. Please help me. Thanks!
Here is my 3 files I have so far...
dvd.h
class dvdArray{
dvd *elt;
int size;
static int defaultSieze;
int getSize();
void display();
dvdArray(unsigned int sz);
dvdArray();
dvdArray(dvdArray &obj);
~dvdArray();
};
dvd.cpp
dvd::dvd(){
id =0;
int n=5;
title = new char [n];
director = new char [n];
title[0] = '\0';
director[0] = '\0';
}
dvd::~dvd(void)
{
}
dvdArray::dvdArray(unsigned int sz){
elt = new dvd[sz];
}
dvdArray::dvdArray(){
size = defaultSieze;
elt = new dvd[defaultSieze];
}
dvdArray::dvdArray(dvdArray &obj){
size = obj.size;
elt = new dvd[defaultSieze];
for (int i=0; i!='\0'; ++i) {
elt[i]=obj.elt[i];
}
}
dvdArray::~dvdArray(void)
{
}
The easiest / cleanest thing to do is to derive from std::vector (or std::array if it suits your purposes better), which is safe as long as you don't then delete the object using a std::vector*, and as long as the functions you want to do checked access accept the parameter as a checked_vector and not a std::vector*/&...
Example:
template <typename T>
class checked_vector : public std::vector<T>
{
public:
// ...forwarding constructors
using std::vector::vector;
T& operator[](size_t n) { return at(n); }
const T& operator[](size_t n) const { return at(n); }
};
Note: that won't protect you from invalid use of iterators, such as incrementing them too far or adding an illegal offset to them.
If - for whatever reason - you're determined to use your own implementation...
dvd& dvdArray::operator[](size_t n)
{
if (n >= size)
throw std::runtime_error("invalid array index");
return dvd[sz];
}
const dvd& dvdArray::operator[](size_t n) const
{
if (n >= size)
throw std::runtime_error("invalid array index");
return dvd[sz];
}
I would do something simple.. General purpose array, bounds checked...
Not sure why you would need that however... Vectors are a very good alternative to arrays.
template <typename T> class myArray{
size_t size;
T *arr;
void allocate(size_t s){
if(s<1) arr = NULL;
else arr = new T[s];
size = s;
}
public:
myArray(){ allocate(10); } //default constructor
myArray(size_t s){ allocate(s); }
~myArray(){ if(arr!=NULL) delete[] arr; arr=NULL; }
T& operator[] (size_t s) {
if(s>=size) throw Error(); //or do whatever
else return arr[s];
}
};
Related
I have been working on a matrix library project similar to the vector library and am currently stuck at iterators. I have been trying to implement a column iterator for quite some time without the use of other libraries (besides iostream and fstream) and my workaround was to have a **columnVector that contains the references of a specified column when colBegin(i) or colEnd(i) is called. The implementation works but I can't manage to make it work aesthetically without going in myMatrix.columnVector as I want it to be encapsulated, rather I want to call matrix::iterator it = myMatrix.colBegin(i) and to iterate with addition and subtraction but my iterator subclass has only a 1d pointer (for the row iterator, hope I can use it for the column iterator as well).
Do you have an idea how to make a column iterator for a dynamically allocated 2d array?
Example of code (trying to keep it as short as possible):
using ll = unsigned long long;
template <typename Type> class matrix
{
// Iterator
public:
class iterator
{
protected:
Type* pointer;
public:
iterator() : pointer(nullptr) {}
iterator(Type* ptr);
Type& operator * ();
...
};
private:
ll numberOfColumns, numberOfRows;
Type** data;
public: // temporary, wish for it to be private
Type** columnVector;
public:
matrix() : numberOfColumns(NULL), numberOfRows(NULL), data(nullptr), columnVector(nullptr) {}
matrix(const ll, const ll);
~matrix();
Type*& operator [] (const ll value);
Type** colBegin(const ll index);
Type** colEnd(const ll index);
...
};
// Thought the constructor might be important since it allocates memory for columnVector as well
template <typename Type> matrix<Type> ::matrix(const ll _numberOfColumns, const ll _numberOfRows)
{
// Copy _numberOfColumns and _numberOfRows into this
numberOfColumns = _numberOfColumns;
numberOfRows = _numberOfRows;
// Allocates memory for data matrix
other.data = (Type**) new Type * [numberOfColumns];
for (size_t i = 0; i < numberOfColumns; i++)
other.data[i] = (Type*) new Type[numberOfRows];
// Fills data matrix with 0
for (size_t i = 0; i < other.numberOfColumns; i++)
for (size_t j = 0; j < other.numberOfRows; j++)
other.data[i][j] = 0;
// Allocation of space for columnVector
columnVector = new Type * [numberOfRows];
}
template <typename Type> Type** matrix<Type> ::colBegin(const ll index)
{
for (size_t i = 0; i < numberOfRows; i++)
columnVector[i] = &data[i][index];
return columnVector;
}
template <typename Type> Type** matrix<Type> ::colEnd(const ll index)
{
for (size_t i = 0; i < numberOfRows; i++)
columnVector[i] = &data[i][index];
return (columnVector + numberOfRows);
}
You don't even need an iterator. Each column is represented with an array. To iterate this array you need to know its beginning and the address of the element after the last one. So the Type* can represent you the iterator:
template <typename Type> class matrix
{
public:
using column_iterator = Type*;
};
So the colBegin/colEnd methods should look like that:
template<typename Type>
matrix<Type>::column_iterator matrix<Type>::colBegin(const ll index) {
return data[index];
}
template<typename Type>
matrix<Type>::column_iterator matrix<Type>::colEnd(const ll index) {
return data[index] + numberOfRows;
}
If you find that the code above iterates throught the row (which is not the case, as your contructor clearly describes data[i] as a column), you may define a class that represents an iterator:
class row_iterator {
public:
row_iterator(Type** data,
size_t rowIndex,
size_t columnIndex)
: m_data(data), m_rowIndex(rowIndex), m_columnIndex(columnIndex) {
}
bool operator == (const row_iterator &other) const {
return other.data == m_data &&
other.m_rowIndex == m_rowIndex &&
other.m_columnIndex == m_columnIndex;
}
row_iterator& operator ++() {
++m_columnIndex;
return *this;
}
row_iterator& operator ++(int) {
row_iterator temp = *this;
++*this;
return temp;
}
Type& operator *() const {
return m_data[m_columnIndex][m_rowIndex];
}
// other operators
private:
Type** m_data;
size_t m_rowIndex;
size_t m_columnIndex;
};
To use this iterator with the container you need to define:
row_iterator rowBegin(size_t rowIndex) {
return row_iterator(data, rowIndex, 0);
}
row_iterator rowEnd(size_t rowIndex) {
return row_iterator(rowIndex, rowIndex, numberOfColumns);
}
I am trying to code a template array class and overloading some operators. Part of my code is as follows:
template.h:
main.cpp:
C2679 binary '<': no operator found which takes a right-hand operand of type 'Array<int>' (or there is no acceptable conversion)
What is causing this error?
What is causing this error?
You are using
return this < a;
this is a pointer while a is a reference to an object. It's analgous to comparing an int* with an int.
int a = 10;
int b = 11;
int* p = &b;
if ( p < a ) { ... }
That is not right.
That function needs to be implemented differently. You need to compare each item of the arrays and return an appropriate value.
template<typename T>
bool Array<T>::operator<(const T& a)
{
int lowerLength = std::min(this->arrLength, a.arrLengh);
for ( int i = 0; i < lowerLength; ++i )
{
if ( this->myArray[i] != a.myArray[i] )
{
return (this->myArray[i] < a.myArray[i]);
}
}
// If we get here, return a value based on which array has more elements.
return (this->arrLength < a.arrLengh)
}
While at it, make the member function a const member function.
bool Array<T>::operator<(const T& a) const;
and change the implementation accordingly.
In findBigPos() (and your other functions in Driver.cpp, too), you should be passing arr by reference, not by pointer. When arr is a pointer, arr[index] is the same as *(arr + index) - it performs pointer arithmetic to dereference the pointer at a given offset, it does not index into your array at all. That is why the compiler thinks you are comparing Array<int> objects, and not calling your operator[].
Try this instead:
#include "wallet.h"
#include "currency.h"
#include "array.h"
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void recurSelectionSort(Array<T>&, int size, int index);
template<typename T>
int findBigPos(Array<T>&, int size, int index);
int main() {
//code
}
template<typename T>
void recurSelectionSort(Array<T>& arr, int size, int index) // move the biggest element in arr to index
{
if (index == size) {
return;
}
else if (index < size) {
int bigPos = findBigPos(arr, size, index); //position of "biggest" element
T bigVal = arr[bigPos]; //the value of "biggest" element
T copy = arr[index]; //copy of wat ever is going to get copy
arr[index] = bigVal;
arr[bigPos] = copy;
recurSelectionSort(arr, size, index + 1);
cout << arr;
}
}
template<typename T>
int findBigPos(Array<T>& arr, int size, int index)
{
if (index == size - 1) {
return index;
}
else
{
int bigPos = findBigPos(arr, size, index + 1);
return arr[bigPos] < arr[index] ? index : bigPos;
}
}
That said, there are some issues with your Array class itself, too.
You are not implementing the Rule of 3/5/0. Your class is lacking a copy constructor and a copy assignment operator, and in C++11 and later a move constructor and a move assignment operator.
you don't have a const version of your operator[] for your operator<< to use, since it takes a reference to a const Array<T> as input.
your operator[] is not checking for index < 0. And it would be better to throw a std::out_of_range exception instead of an int. If it throws at all. Typically, an array's operator[] should not perform bounds checking at all. That is why containers like std::vector and std::string have a separate at() method for handling bounds checking.
your operator< is not implemented correctly at all. You can't compare a Array<T>* pointer to a const T& reference. You probably meant to dereference the this pointer before comparing it to a, but then that would lead to an endless recursive loop. What you should do instead is change const T& a to const Array<T> &a and then compare the contents of this to the contents of a.
Try this:
#ifndef ARRAY_HEADER
#define ARRAY_HEADER
#include <iostream>
#include <stdexcept>
#include <utility>
template<typename T>
class Array
{
private:
int arrLength;
T* myArray;
public:
Array(int length = 5);
Array(const Array &a);
Array(Array &&a);
virtual ~Array();
int getLength() const;
Array& operator=(Array a);
T& operator[](int index);
const T& operator[](int index) const;
bool operator<(const Array &a) const;
friend std::ostream& operator<<(std::ostream &output, const Array &arr)
{
int arrSize = arr.getLength();
for (int i = 0; i < arrSize; i++) {
output << arr[i] << " ";
}
return output;
}
};
template<typename T>
Array<T>::Array(int length)
{
myArray = new T[length];
arrLength = length;
}
template<typename T>
Array<T>::Array(const Array<T> &a)
{
myArray = new T[a.arrLength];
arrLength = a.arrLength;
for(int i = 0; i < arrLength; ++i)
myArray[i] = a.myArray[i];
}
template<typename T>
Array<T>::Array(Array<T> &&a)
{
arrLength = a.arrLength;
myArray = a.myArray;
a.myArray = nullptr;
a.arrLength = 0;
}
template<typename T>
Array<T>::~Array()
{
delete[] myArray;
}
template<typename T>
int Array<T>::getLength() const
{
return arrLength;
}
template<typename T>
Array<T>& Array<T>::operator=(Array<T> a)
{
using std::swap;
swap(myArray, a.myArray);
swap(arrLength, a.arrLength);
return *this;
}
template<typename T>
T& Array<T>::operator[](int index) {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
const T& Array<T>::operator[](int index) const {
if ((index < 0) || (index >= arrLength)) {
throw std::out_of_range("index is out of range");
}
return myArray[index];
}
template<typename T>
bool Array<T>::operator<(const Array<T> &a) const
{
if (arrLength < a.arrLength)
return true;
if (arrLength == a.arrLength)
{
for (int i = 0; i < arrLength; ++i)
{
if (myArray[i] != a.myArray[i])
return myArray[i] < a.myArray[i];
}
}
return false;
}
#endif
I'm trying to overload the [] operator in a templated dynamic array, however it doesn't seem to be doing anything?
I created a templated dynamic array for school, I've tried separating the overload to outside the class.
The DynArray.h
template <typename T>
class DynArray
{
public:
//The constructor initialises the size of 10 and m_Data to nullptr
DynArray(void)
{
m_AllocatedSize = 10;
m_Data = nullptr;
}
//deletes m_Data
~DynArray()
{
delete[] m_Data;
m_Data = nullptr;
}
T* operator [] (int index)
{
return m_Data[index];
}
//creates the array and sets all values to 0
T* CreateArray(void)
{
m_Data = new T[m_AllocatedSize];
m_UsedElements = 0;
for (int i = 0; i < m_AllocatedSize; ++i)
{
m_Data[i] = NULL;
}
return m_Data;
}
private:
bool Compare(T a, T b)
{
if (a > b)
return true;
return false;
}
T* m_Data;
T* m_newData;
int m_AllocatedSize;
int m_UsedElements;
};
Main.cpp
#include <iostream>
#include "DynArray.h"
int main()
{
DynArray<int>* myArray = new DynArray<int>;
//runs the create function
myArray->CreateArray();
int test = myArray[2];
delete myArray;
return 0;
}
I expected the overload to return in this case the int at m_Data[2], however it doesn't seem to overload the [] at all instead says no suitable conversion from DynArray<int> to int.
You are returning a pointer which is not what you want. You should do like this:
T& operator [] (const int& index)
{
return m_Data[index];
}
Also myArray is a pointer you have to dereference it before using.
int test = (*myArray)[2];
It's better to not to use pointer:
int main()// suggested by #user4581301
{
DynArray<int> myArray;
//runs the create function
myArray.CreateArray();
int test = myArray[2];
return 0;
}
There is no reason for using pointers here.
Instead of new and delete for dynamic allocation it is better to use smart pointers.
There is also one issue here, you are not chacking the range and what if theindex was for example a negative number.
I'm porting code that uses a very large array of floats, which may trigger malloc failures from c to c++. I asked a question about whether I should use vectors or deques and Niki Yoshiuchi generously offered me this example of a safely wrapped type:
template<typename T>
class VectorDeque
{
private:
enum TYPE { NONE, DEQUE, VECTOR };
std::deque<T> m_d;
std::vector<T> m_v;
TYPE m_type;
...
public:
void resize(size_t n)
{
switch(m_type)
{
case NONE:
try
{
m_v.resize(n);
m_type = VECTOR;
}
catch(std::bad_alloc &ba)
{
m_d.resize(n);
m_type = DEQUE;
}
break;
}
}
};
I needed a 2D vector of vectors/deque of deques, so I modified it to the following code:
template<typename T>
class VectorDeque
{
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
std::deque<std::deque<T> > x_d,y_d,z_d;
std::vector<std::vector<T> > x_v,y_v,z_v;
TYPE my_container;
public:
void resize(size_t num_atoms, size_t num_frames)
{
switch(m_type)
{
case NONE:
try
{
x_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_v[counter].resize(num_frames);
y_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_v[counter].resize(num_frames);
z_v.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_v[counter].resize(num_frames);
my_container = VECTOR;
}
catch(std::bad_alloc &e)
{
x_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
x_d[counter].resize(num_frames);
y_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
y_d[counter].resize(num_frames);
z_d.resize(num_atoms);
for (unsigned int couter=0;couter < num_frames; counter++)
z_d[counter].resize(num_frames);
my_container = DEQUE;
}
break;
}
}
};
I now want to be able to define my bracket operators so that I can have a statement like
x[1][2] directly access whichever is the real memory container I'm using (given by the value of my enumerated variable.
I've seen a couple of tutorials floating around about overriding the brackets operator, but have positively no idea to override double brackets.
How can you overload double brackets?
Additionally, how would you overload double iterators (in case I want to use an iterator, as opposed to direct indexing)?
EDIT 1:
Based on the solution from Martin York/Matteo Italia I devised the following class:
template<typename T>
class VectorDeque2D
{
public:
class VectorDeque2D_Inner_Set
{
VectorDeque2D& parent;
int first_index;
public:
// Just init the temp object
VectorDeque2D_Inner_Set(My2D& p, int first_Index) :
parent(p),
first_Index(first_index) {}
// Here we get the value.
T& operator[](int second_index) const
{ return parent.get(first_index,second_index);}
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via
// operator[]
VectorDeque2D_Inner_Set operator[](unsigned int first_index) {
return (*this, x);
}
void resize_first_index(unsigned int first_index) {
try {
my_vector.resize(first_index);
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
my_container = DEQUE;
}
}
void resize_second_index(unsigned int second_index) {
try {
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
void resize(unsigned int first_index,
unsigned int second_index) {
try {
my_vector.resize(first_index);
for (unsigned int couter=0;couter < my_vector.size(); counter++) {
my_vector[counter].resize(second_index);
}
my_container = VECTOR;
}
catch(std::bad_alloc &e) {
my_deque.resize(first_index);
for (unsigned int couter=0;couter < my_deque.size(); counter++) {
my_deque[counter].resize(second_index);
}
my_container = DEQUE;
}
}
private:
enum STORAGE_CONTAINER { NONE, DEQUE, VECTOR };
friend class VectorDeque2D_Inner_Set;
std::vector<std::vector<T> > my_vector;
std::deque<std::deque<T> > my_deque;
STORAGE_CONTAINER my_container;
T& get(int x,int y) {
T temp_val;
if(my_container == VECTOR) {
temp_val = my_vector[first_index][second_index];
}
else if(my_container == DEQUE) {
temp_val = my_deque[first_index][second_index];
}
return temp_val;
}
};
Finally a size-safe 2D container!! Thanks guys!
There are two main techniques:
1) Use operator() rather than operator[].
This is because the operator() allows multiple parameters.
class My2D
{
public:
int& operator()(int x,int y) { return pget(x,y);}
private:
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
2) Use operator[] but return an intermediate object.
You can then apply the second operator[] to the intermediate object.
class My2D
{
public:
class My2DRow
{
My2D& parent;
int x;
public:
My2DRow(My2D& p, int theX) : parent(p), x(theX) {} // Just init the temp object
int& operator[](int y) const { return parent.pget(x,y);} // Here we get the value.
};
// Return an object that defines its own operator[] that will access the data.
// The temp object is very trivial and just allows access to the data via operator[]
My2DRow operator[](int x) { return My2DRow(*this, x);}
private:
friend class My2DRow;
int& pget(int x,int y) { /* retrieve data from 2D storage */ }
};
int main()
{
My2D data;
int& val = data[1][2]; // works fine.
// This is the same as
My2D::My2DRow row = data[1];
int& val2 = row[2];
}
I prefer the second technique.
This is because it leaves the original code untouched and more natural to read (in an array context). Of course you pay for the simplicity at the high level with slightly more complex code implementing your 2D array.
How can you overload double brackets?
I didn't fully understand your question, but you have to overload brackets, and make them return an object who overloads its own bracket operator.
For example, if you have a vector of vectors, the work is already done: vector < vector < something > > overloads operator[], which returns a vector< something >; this, in turn, has its bracket operator overloaded (and it returns a something object), so you can simply do:
vector<vector<something> > vec;
// ...
something s = vec[2][3];
Example with a proxy object:
template <typename T>
class Container
{
private:
// ...
public:
// Proxy object used to provide the second brackets
template <typename T>
class OperatorBracketHelper
{
Container<T> & parent;
size_t firstIndex;
public:
OperatorBracketHelper(Container<T> & Parent, size_t FirstIndex) : parent(Parent), firstIndex(FirstIndex) {}
// This is the method called for the "second brackets"
T & operator[](size_t SecondIndex)
{
// Call the parent GetElement method which will actually retrieve the element
return parent.GetElement(firstIndex, SecondIndex);
}
}
// This is the method called for the "first brackets"
OperatorBracketHelper<T> operator[](size_t FirstIndex)
{
// Return a proxy object that "knows" to which container it has to ask the element
// and which is the first index (specified in this call)
return OperatorBracketHelper<T>(*this, FirstIndex);
}
T & GetElement(size_t FirstIndex, size_t SecondIndex)
{
// Here the actual element retrieval is done
// ...
}
}
(add overloaded const methods wherever appropriate :) )
Note that with this method you lose almost nothing in respect to an operator() implementation, since the retrieval is still done in one single place, without constraints on the usage of the two indexes, having both indexes at the moment of performing the retrieval, and without returning "fat" temporary objects (OperatorBracketHelper is just as big as two pointers, and can be easily optimized away by the compiler).
There is no "double brackets" operator in C++. What you need to do is define a single [] operator and have it return a reference to another object, which can in turn respond to its own [] operator. This can be nested as many levels deep as you require.
For example, when you create a vector of vectors, the [] operator on the outer vector returns a reference to one of the inner vectors; the [] operator on that vector returns a reference to an individual element of the vector.
std::vector<std::vector<float> > example;
std::vector<float> & first = example[0]; // the first level returns a reference to a vector
float & second = example[0][0]; // the same as first[0]
Don't overload the [] operator, overload the () operator.
See this link:Overloading Subscript Operator.
I highly suggest reading through the C++ FAQ Lite at least once before posting to Stack Overflow. Also, searching Stack Overflow may yield some useful information also.
I covered overloading operator[] for a multi-dimensional array in an answer to a previous question.
I'd probably deal with iterators pretty similarly: Have one iterator that represents a "slice" (row or column) of the multi-dimensional array, and then another that represents an element in that slice.
So I do have an array class made by myself:
#include <algorithm>
template <class T>
class myArray
{
private:
int length;
T *elements;
public:
myArray()
{
this->length = 0;
this->elements = nullptr;
}
myArray(int len)
{
this->length = len;
this->elements = new T[len];
}
myArray(int* data, int len)
{
this->length = len;
this->elements = new T[len];
std::copy(data, data+len, this->elements);
}
~myArray()
{
delete[] this->elements;
}
};
I think that for now it works. I wanted to check if the elements I pass in the 3rd constructor are getting properly copied, so I wrote this:
int data[] = {1,2,3,4,5};
myArray<int> a (data, 5);
for (auto x: myArray)
{
std::cout << x << '\n';
}
The question is what I have to write in the class, to make it return this->elements, when I just call myArray. I know that it is possibly trivial, but I don't know how to find it, actually how to call it, to make Google find the answer for me.
You need to implement begin and end member functions for your class (or as global functions in the same namespace as your class), which return iterators pointing to the first element, and one past the last element of your array. Since your class is backed by a contiguous array, you don't need to implement an iterator class, and can just use pointers.
// these are member functions, as they would be defined inside your class
T* begin() {
return elements;
}
T* end() {
return elements + length;
}
You should also implement const versions.
T const* begin() const {
return elements;
}
T const* end() const {
return elements + length;
}