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;
}
Related
So I tried to make a custom string & vector class (from a youtube channel named The Cherno). But when I tried to make a copy constructor on that vector class with my custom string class, I get this exception: str was nullptr (Occured in the string copy constructor) and also when I tried with a primitive data types (int) I also get this exception: std::move<int & __ptr64>(...) returned nullptr (Occured on the re_allocate function).
main.cpp:
int main() {
utils::list<int> list;
list.place(5);
utils::list<int> other = list; // Works fine if I remove this line
}
String copy constructor:
utils::string::string(const string& other)
: pr_Length(other.pr_Length)
{
// If the other string was not initialized then return
if (!other.pr_Init) {
pr_Init = false;
return;
}
// Allocates a new char*
pr_Data = new char[pr_Length + 1];
// Copy all the char* on other string and copy it here
std::memcpy(pr_Data, other.pr_Data, pr_Length + 1);
// This string is initialized
pr_Init = true;
}
Vector class:
template<typename T>
class list {
private:
T* pr_Array;
size_t pr_Size;
size_t pr_Cap;
public:
using ValType = T;
using Iterator = list_iterator<list<T>>;
list() {
re_allocate(2);
}
~list() {
destroy_all();
::operator delete(pr_Array, pr_Cap * sizeof(T));
}
list(const list& other)
: pr_Size(other.pr_Size), pr_Cap(other.pr_Cap)
{
re_allocate(pr_Cap);
}
void add(const T& elem) {
if (pr_Size >= pr_Cap) {
re_allocate(pr_Cap + 2);
}
new(&pr_Array[pr_Size]) T(std::move(elem));
pr_Size++;
}
void add(T&& elem) {
if (pr_Size >= pr_Cap) {
re_allocate(pr_Cap + 2);
}
new(&pr_Array[pr_Size]) T(std::move(elem));
pr_Size++;
}
template<typename...Args>
T& place(Args&&... args) {
if (pr_Size >= pr_Cap) {
re_allocate(pr_Cap + 2);
}
new(&pr_Array[pr_Size]) T(std::forward<Args>(args)...);
return pr_Array[pr_Size++];
}
void destroy_back() {
if (pr_Size == 0) {
return;
}
pr_Array[pr_Size].~T();
pr_Size--;
}
void destroy_all() {
for (size_t i = 0; i < pr_Size; i++) {
pr_Array[i].~T();
}
pr_Size = 0;
}
const T& operator[](size_t i) const {
return pr_Array[i];
}
T& operator[](size_t i) {
return pr_Array[i];
}
const size_t size() const {
return pr_Size;
}
size_t size() {
return pr_Size;
}
Iterator begin() {
return Iterator(pr_Array);
}
Iterator end() {
return Iterator(pr_Array + pr_Size);
}
private:
void re_allocate(size_t cap) {
T* newBlock = (T*)::operator new(cap * sizeof(T));
if (cap < pr_Size) {
pr_Size = cap;
}
for (size_t i = 0; i < pr_Size; i++) {
new(newBlock + i) T(std::move(pr_Array[i]));
}
for (size_t i = 0; i < pr_Size; i++) {
pr_Array[i].~T();
}
::operator delete(pr_Array, pr_Cap * sizeof(T));
pr_Array = newBlock;
pr_Cap = cap;
}
};
utils::list<int> list;
main() default-constructs an instance of this template. Let's inspect the template and its default constructor, then.
T* pr_Array;
size_t pr_Size;
size_t pr_Cap;
This template class declares these members.
list() {
The constructor does not have a member initialization section, and these three members do not have a default value, so all of these three class members are not initialized to anything. Their initial value is random garbage.
re_allocate(2);
The constructor then calls re_allocate(2).
if (cap < pr_Size) {
re_allocate, at this point, compares its parameter, cap, against pr_Size. But if you've been keeping careful notes, you will then note that pr_Size was not initialized to anything. From this point on, everything is undefined behavior.
There may or may not be additional instances of undefined behavior in this tempalte class, but given that this undefined behavior always occurs in the constructor, no further analysis is possible to determine that.
For example, pr_reallocate refers to pr_Array too. The current state of affairs is that pr_Array is also uninitialized and contains random garbage, so there's that as well. Whether or not this remains undefined behavior depends on how the initial occurence of undefined behavior gets fixed.
Then there's potential undefined behavior in the copy-constructor... But, one step at a time...
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';
}
}
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.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I was giving a adventurer class containing a bunch of functions and member variables.
One of them is:
string*** items;
so first I thought it was a 3d array that I have to make but I was told that it suppose to be a pointer to a 2d array.
What I tried to do was make a temp array
string** temp;
Init that and fill it, I then point my items to temp
items = &temp;
This works till the function exits. Then we I try and call a index value inside items
cout<<*item[0][0];
there is nothing. When temp disappears so does the array.
This line also doenst work
(*items) = new string*[2];
I couldn't find anything online that helped me.
How can I initialize items or keep the array data that i made using temp.
For those asking for the code, this is what they gave me:
class Adventurer{
private:
string*** items;
string name;
double maxCarryWeight;
double currentCarryWeight;
int currentNumberOfItems;
int maxNumberOfItems;
double health;
static int numberOfAdventurers;
public:
Adventurer(); //default constructor
Adventurer(const Adventurer& a); //copy constructor
~Adventurer();
bool pickUpItem(string it, double weight);
bool dropItem(string it);
bool dropItem(int index);
void setName(string n);
string getName() const;
void setMaxCarryWeight(double w);
double getMaxCarryWeight() const;
void setCurrentCarryWeight(double w);
double getCurrentCarryWeight() const;
void setMaxNumberOfItems(int n);
int getMaxNumberOfItems() const;
void setCurrentNumberOfItems(int n);
int getCurrentNumberOfItems() const;
int getNumberOfAdventurers() const;
void setHealth(double h);
double getHealth() const;
string** getItem(int index) const;
Adventurer& operator = (const Adventurer& a);
};
And said that
string*** items;
is a pointer to a 2d array
It's not entirely clear what you are trying to achieve from your question but it seems your main issue is likely to do with returning the address of a local variable when attempting to allocate your 2D C-style std::string array. Below is a very basic example of how to avoid such an issue via returning the allocated 2D array and then taking the address of this returned value and storing it in your std::string*** items variable.
// allocate memory for a 2D C-style array of std::string's
std::string** allocate_2d_array(std::size_t rows, std::size_t cols) {
std::string** items_arr = new std::string*[rows];
for (std::size_t i = 0; i < rows; ++i)
items_arr[i] = new std::string[cols];
return items_arr;
}
// print each element of the 2D C-style array via a pointer to the array
void print_items(std::ostream& os, std::string*** items, std::size_t rows, std::size_t cols) {
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j)
os << (*items)[i][j] << ' ';
os << '\n';
}
}
// destruct the 2D C-style array
void deallocate_2d_array(std::string** items_arr, std::size_t rows, std::size_t cols) {
for (std::size_t i = 0; i < rows; ++i)
delete[] items_arr[i];
delete[] items_arr;
}
int main(void) {
std::size_t rows = 3; // matrix rows
std::size_t cols = 3; // matrix columns
// allocate a 2D array of std::string's
std::string** items_arr = allocate_2d_array(items, 3, 3);
// set the pointer to a 2D std::string array to address of items_arr
std::string*** items = &items_arr;
int count = 0;
// fill items_arr with data via items pointer
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j)
(*items)[i][j] = std::to_string(++count);
}
print_items(std::cout, items); // print matrix to terminal
deallocate_2d_array(items_arr, rows, cols); // deallocate items_arr
}
However, as mentioned in the comments, this is not in keeping with modern c++ and one would much rather use a std::vector<std::vector<std::string>> to store a matrix of std::string instances.
You mentioned that using std::vector is not an option but I suspect that your teacher probably didn't say anything about making your own barebones dynamic array with similar semantics to a std::vector so that is always one way around these silly restrictions. With that in mind, below is the framework for a very basic (and untested) class which mimics a std::vector (without using allocators) which would make your task much simpler.
template<typename Ty>
class dynamic_array {
public:
typedef Ty value_type;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty* pointer;
typedef const Ty* const_pointer;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// CONSTRUCTION/ASSIGNMENT
dynamic_array()
: arr_capacity(0), arr_size(0) {}
dynamic_array(size_type count)
: arr_capacity(count), arr_size(count) { allocate(count); }
~dynamic_array() { destroy(); }
dynamic_array& operator=(dynamic_array _other) {
swap(*this, _other);
return *this;
}
// CAPACITY
bool empty() const noexcept { return arr_size; }
size_type size() const noexcept { return arr_size; }
size_type capacity() const noexcept { return arr_capacity; }
void reserve(size_type new_cap) { if (new_cap > arr_capacity) reallocate(new_cap); }
// ELEMENT ACCESS
reference operator[](size_type n) { return arr[n]; }
const_reference operator[](size_type n) const { return arr[n]; }
// MODIFIERS
void clear() {
for (size_type i = 0; i < arr_size; ++i)
(&arr[i])->~value_type();
arr_size = 0;
}
void push_back(const value_type& _val) {
if (arr_size == arr_capacity) // TODO: expand arr using reallocate
pointer val = new (arr + arr_size) value_type(_val);
++arr_size;
}
void pop_back() {
(&arr[arr_size-1])->~value_type();
--arr_size;
}
void swap(dynamic_array& _other) {
std::swap(arr, _other.arr);
std::swap(arr_capacity, _other.arr_capacity);
std::swap(arr_size, _other.arr_size);
}
static void swap(dynamic_array& lhs, dynamic_array& rhs) { lhs.swap(rhs); }
private:
value_type* arr;
size_type arr_capacity;
size_type arr_size;
void allocate(size_type n) { arr = new value_type[n]; }
void reallocate(size_type new_cap) {
value_type* tmp = new value_type[new_cap];
size_type tmp_rows = (new_cap > arr_capacity) ? arr_capacity : new_cap;
for (size_type i = 0; i < tmp_rows; ++i)
tmp[i] = std::move(arr[i]);
delete[] arr;
arr = tmp;
arr_capacity = new_cap;
}
void destroy { clear(); delete[] arr; }
};
Then instead of messing around with lots of raw pointers and the headaches they bring, you can just pass around your dynamic_array<dynamic_array<std::string>> class instance without needing to worry about memory management.
Note: The above dynamic_array class is untested and probably requires some tweaks, it is also not a great example of implementing a STL-style container (you'd need allocator and iterator support), it is just intended as a barebones std::vector mimicking container to get around the "no vector" task requirement.
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];
}
};