Operator overloading [] in templated Dynamic Array - c++

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.

Related

Why is stack smashing detected if an empty array[] is initiated in the constructor < 2 inside a class

I am running into this error...
...with this minimal executable code to reproduce the error and ...
#include <iostream>
class arrayClass
{
private:
int _elements;
int _array[];
public:
arrayClass()
: _elements(32)
{
//If i is smaller than 2 the "Stack Smashing Detected" error shows up, why is that?
//If i is 2 or more, no error appears
//(f.e. int i = 0 or int i = 1 doesn't work, int i = 2 or higher works - why is that?)
for(int i = 0; i < _elements; ++i){
_array[i] = 0;
}
}
int get(int index){
return _array[index];
}
};
int main()
{
arrayClass arr;
std::cout << arr.get(2) << std::endl;
return 0;
}
...it doesn't matter if I initiate _elements with the initialization list or at with the
attribute itself with f.e.32 or whatever number.
If I pass the int value to construct the arrayClass(int) in addition to the arrayClass() constructor, the error doesn't shows up.
If I construct the arrayClas(int) with a value alone, it can also just be used with the 2nd slot upwards.
So my question is:
Why couldn't I initiate the 1st and 2nd array slot of a default array[]?
Or the other way around, why is it possible to assign an empty array[] to a class without a value and not f.e. _array[32] without an error but the one with assigning array[0] = 0; and array[1] = 0; ?
(And yes, I am aware of vectors, I need to use arrays for various reasons)
Because you never allocate memory for the array to begin with, everything is undefined behavior. I don't even know what int _array[] evaluates to without a size specifier. I'll look that up later.
Change your construct code to "new" the array. And have a destructor to delete it.
class arrayClass
{
private:
int _elements;
int* _array;
public:
arrayClass()
: _elements(32)
{
_array = new int[_elements];
memset(_array, '\0', sizeof(array[0])*_elements);
}
int get(int index){
return _array[index];
}
~arrayClass()
{
delete [] _array;
}
};
Or if you can have a fixed number of elements, explictly size the array when it's declared:
class arrayClass
{
private:
int _array[32];
public:
arrayClass()
: _array() // this will zero-init the array
{
}
int get(int index){
return _array[index];
}
};
int _array[]; is a flexible array and isn't allowed in standard C++. It does not allocate memory so when you access any element in the array, you have undefined behavior.
I am aware of vectors, I need to use arrays for various reasons
In reality there are very few valid reasons so I expect the various reasons you mention to be artificial. If you need to pass the data to a function, like void func(int*, size_t elements);, you can still use a std::vector<int>. Just pass its data() and size() as arguments.
In C++ you should typically use a std::vector<int> for cases like this.
Example:
#include <iostream>
#include <vector>
class arrayClass
{
private:
std::vector<int> _array;
public:
arrayClass(size_t s = 32)
: _array(s)
{}
size_t size() const {
return _array.size();
}
int get(size_t index) const {
return _array[index];
}
};
int main()
{
arrayClass arr;
std::cout << arr.get(10) << std::endl;
return 0;
}
An alternative, if your arrayClass has a fixed number of elements:
#include <array>
class arrayClass
{
private:
std::array<int, 32> _array;
public:
arrayClass()
: _array{}
{}
size_t size() const {
return _array.size();
}
int get(size_t index){
return _array[index];
}
};
If the extra space a std::vector consumes (usually 4 or 8 bytes) is a real concern, you could make a similar class that only stores the pointer to the allocated memory and the size. It could look like this (but doesn't have the ability to grow/shrink like a vector has):
#include <iostream>
#include <algorithm>
#include <memory>
#include <type_traits>
template<typename T, std::enable_if_t<std::is_default_constructible_v<T>>* = nullptr>
class arrayClass {
public:
using value_type = T;
arrayClass(size_t size = 32) :
_size(size),
_array(std::make_unique<T[]>(_size))
{}
// copy constructor
arrayClass(const arrayClass& rhs) :
_size(rhs._size),
_array(std::make_unique<T[]>(_size))
{
static_assert(std::is_copy_assignable_v<T>, "T must be copy assignable");
std::copy(rhs._array.get(), rhs._array.get() + _size, _array.get());
}
arrayClass(arrayClass&&) = default; // move constructor
// copy assignment operator
arrayClass& operator=(const arrayClass& rhs) {
*this = arrayClass(rhs); // copy construct and move assign
return *this;
}
arrayClass& operator=(arrayClass&&) = default; // move assignment operator
// accessing element at index
T& operator[](size_t index) { return _array[index]; }
const T& operator[](size_t index) const { return _array[index]; }
// bounds checking access to element
T& at(size_t idx) {
if(idx >= _size)
throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
return _array[idx];
}
const T& at(size_t idx) const {
if(idx >= _size)
throw std::out_of_range(std::to_string(idx) + ">=" + std::to_string(_size));
return _array[idx];
}
size_t size() const { return _size; }
// support for iterating over the elements in the array
const T* cbegin() const { return _array.get(); }
const T* cend() const { return _array.get() + _size; }
const T* begin() const { return cbegin(); }
const T* end() const { return cend(); }
T* begin() { return _array.get(); }
T* end() { return _array.get() + _size; }
private:
size_t _size;
std::unique_ptr<T[]> _array;
};
using intArray = arrayClass<int>;
int main() {
try {
intArray arr1(10);
// the added iterator support makes range-based for-loops possible:
for(int& v : arr1) {
static int i=0;
v = ++i;
}
intArray arr2;
arr2 = arr1; // copy assign
for(size_t i=0; i < arr2.size(); ++i) {
std::cout << arr2[i] << '\n'; // access using operator[] works too
}
std::cout << arr2.at(10) << '\n'; // throws out_of_range exception
}
catch(const std::out_of_range& ex) {
std::cerr << ex.what() << '\n';
}
}

how to implement efficient add in C++ templated list class

Suppose we have a templated c++ list class. Yes, vector exists, but the point is to know how to handle this problem.
The Constructor allocates a block of n objects of type T but does not initialize because they are not used yet.
In the add method, we wish to copy in a new object, but using operator = is not possible because operator = would first destroy the existing object, which was never initialized. How does one copy in an object into data[used] ?
#include <string>
template<typename T>
class DynArray {
private:
int capacity;
int used;
T* data;
public:
DynArray(int initialCap) : capacity(initialCap), used(0), data((T*)new char[sizeof(T)*capacity]) {}
void add(const T& e) {
//TODO: if the dynarray is full, grow
data[used++] = e; //ERROR! Should use copy constructor!!!
}
};
int main() {
DynArray<std::string> a(5);
a.add(std::string("abc"));
}
You should use placement new:
void add(const T& e) {
//TODO: if the dynarray is full, grow
new (data + used) T(e);
used++;
}
Placement new constructs an object in already allocated memory.
For what you are attempting to do, you need to call T's copy constructor using placement-new. And don't forget to implement the Rule of 3/5/0 as well:
template<typename T>
class DynArray {
private:
int capacity;
int used;
T* data;
public:
DynArray(int initialCap = 0) : capacity(0), used(0), data(0) {
reserve(initialCap);
}
DynArray(const DynArray &src) : capacity(0), used(0), data(0) {
reserve(src.capacity);
for(int i = 0; i < src.used; ++i) {
add(src.data[i]);
}
}
// C++11 and higher only...
DynArray(DynArray &&src) : capacity(src.capacity), used(src.used), data(src.data) {
src.capacity = src.used = 0;
src.data = 0;
}
~DynArray() {
clear();
delete[] reinterpret_cast<char*>(data);
}
DynArray& operator=(const DynArray &rhs) {
if (&rhs != this) {
DynArray(rhs).swap(*this);
}
return *this;
}
// C++11 and higher only...
DynArray& operator=(DynArray &&rhs) {
DynArray(std::move(rhs)).swap(*this);
return *this;
}
void swap(DynArray &other) {
std::swap(data, other.data);
std::swap(used, other.used);
std::swap(capacity, other.capacity);
}
void clear() {
resize(0);
}
void reserve(int newCap) {
// TODO: round up newCap to an even block size...
if (newCap <= capacity) return;
T *newData = reinterpret_cast<T*>(new char[sizeof(T) * newCap]);
for(int i = 0; i < used; ++i) {
new (newData + i) T(data[i]);
}
delete[] reinterpret_cast<char*>(data);
data = newData;
capacity = newCap;
}
void resize(int newSize) {
if (newSize < 0) newSize = 0;
if (newSize == used) return;
if (newSize > used) {
reserve(newSize);
for(int i = used; i < newSize; ++i) {
new (data + i) T();
++used;
}
}
else {
for(int i = used-1; i >= newSize; --i) {
data[i]->~T();
--used;
}
}
}
void add(const T& e) {
if (used == capacity) {
reserve(capacity * 1.5);
}
new (data + used) T(e);
++used;
}
};
#include <string>
int main() {
DynArray<std::string> a(5);
a.add("abc");
}
The DynArray class has a type of T, so you should simply allocate an array of type T with size of initialCap, which is simply
new T[initialCap];
For built-in types, e.g. int, the elements are left uninitialized.
For others, like string, the default constructor of T is called to initialize the elements.
In the add method, we wish to copy in a new object, but using operator = is not possible because operator = would first destroy the existing object
data[used++] = e; This is perfectly fine. It assigns e to data[used] - calls the assignment operator of the string, and it won't cause any troubles. However, when your array grows, you would need to allocate new arrays with double capacity, copy over the elements, and destroy the old data.

Getting object data by just calling a object

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;
}

Overloading operator [] in class, so it can return object from array that is in template class

template <class T>
class Planer{
T array;
int max_stavki;
int broj_stavki;
public:
Planer()
{
max_stavki = 100;
array = new T[max_stavki];
broj_stavki = 0;
}
~Planer()
{
delete[] array;
}
void add(T& x){
if (broj_stavki==max_stavki){
throw NO_SPACE();
}
else{
array[broj_stavki] = x; // here i get error
broj_stavki++;
}
}
};
In template class i need to create array of objects of this class.
class Ispit{
char* naziv;
int sifra = -0001;
char* datum;
int vreme;
};
But when i want to use function add from template class this error pops up:
Error C2676 binary '[': 'Ispit' does not define this operator or a conversion to a type acceptable to the predefined operator
I tried something like this in template class. But error is still there.
Planer<T>& operator [] (int x){
return array[x];
};
I think i need to overload operator [] in class Ispit too but i don't know how.
Thank you guys in front!
Planer<Ispit>'s array member is of type Ispit, which isn't actually an array. What did you intend for the type of array to be? Potentially:
template <class T>
class Planer
{
T* array; // NOTE THAT I MADE THIS A POINTER
int max_stavki;
int broj_stavki;
public:
Planer() {
array = nullptr;
broj_stavki = 0;
max_stavki = 0;
}
~Planer() {
clear();
delete[] array;
}
Planer(Planer&& other) noexcept {
array = other.array;
broj_stavki = other.broj_stavki;
max_stavki = other.max_stavki;
other.array = nullptr;
other.broj_stavki = 0;
other.broj_stavki = 0;
}
Planer& operator=(Planer&& other) noexcept {
std::swap(array, other.array);
std::swap(array, other.broj_stavki);
std::swap(array, other.max_stavki);
other.clear();
}
};

Custom index operator C++

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];
}
};