As an exercise, I'm trying to create a class myArray that acts as a simplified array class. Here is my header:
#ifndef myArray_h
#define myArray_h
typedef double ARRAY_ELEMENT_TYPE;
class myArray {
public:
//--constructors
myArray(int initMax);
// post: Allocate memory during pass by value
myArray(const myArray & source);
// post: Dynamically allocate memory during pass by value
//--destructor
~myArray();
// post: Memory allocated for my_data is deallocated.
//--modifier
void set(int subscript, ARRAY_ELEMENT_TYPE value);
// post: x[subscript] = value when subscript is in range.
// If not, an error message is displayed.
//--accessor
ARRAY_ELEMENT_TYPE sub(int subscript) const;
// post: x[subscript] is returned when subscript is in range.
// If not, display an error message and return [0].
private:
ARRAY_ELEMENT_TYPE* my_data;
int my_capacity;
};
#endif
Here is my implementation:
#include "myArray.h"
#include <iostream>
#include <cstring>
using namespace std;
typedef double ARRAY_ELEMENT_TYPE;
//--constructors
myArray::myArray(int initMax)
{
my_capacity = initMax;
}
myArray::myArray(const myArray & source)
{
int i;
my_data = new ARRAY_ELEMENT_TYPE[source.my_capacity];
for(i=0; i < my_capacity; i++)
my_data[i] = source.sub(i);
}
//--destructor
myArray::~myArray()
{
delete [ ] my_data;
}
//--modifier
void myArray::set(int subscript, ARRAY_ELEMENT_TYPE value)
{
if(subscript > my_capacity - 1)
{
cout << "**Error: subscript " << subscript << " not in range 0.." << my_capacity-1 << ". The array is unchanged." << endl;
}
else
my_data[subscript] = value;
}
//--accessor
ARRAY_ELEMENT_TYPE myArray::sub(int subscript) const
{
if(subscript >= my_capacity)
{
cout << "**Error: subscript " << subscript << " not in range 0.." << my_capacity-1 << ". Returning first element." << endl;
cout << my_data[0];
}
else
{
return my_data[subscript];
}
}
And I'm using this as a test driver:
#include <iostream>
using namespace std;
typedef double ARRAY_ELEMENT_TYPE;
#include "myArray.h"
void show (const myArray & arrayCopy, int n)
{
for(int j = 0; j < n; j++)
cout << arrayCopy.sub(j) << endl;
}
int main()
{
int n = 6;
myArray a(6);
a.set(0, 1.1);
a.set(1, 2.2);
a.set(2, 3.3);
a.set(3, 4.4);
a.set(4, 5.5);
a.set(5, 6.6);
show(a, n);
cout << a.sub(11) << endl;
a.set(-1, -1.1);
return 0;
}
The problem is that when I run this, I get nothing for a bit, then the "Press any key to continue..." prompt. What's going wrong?
The myArray constructor doesn't allocate memory for my_data. The first time you call set, it attempts to write to an uninitialised pointer. This results in undefined behaviour but a crash is likely.
You should change the constructor to
myArray::myArray(int initMax)
{
my_capacity = initMax;
my_data = new ARRAY_ELEMENT_TYPE[my_capacity];
}
There are a couple of other issues with the code you could also consider
In 'set', the test
if(subscript > my_capacity - 1)
should be
if(subscript < 0 || subscript > my_capacity - 1)
Or you could change the subscript argument to have type unsigned int.
In sub, the line cout << my_data[0]; should presumably be return my_data[0];
myArray::myArray(int initMax)
{
my_capacity = initMax;
my_data = new ARRAY_ELEMENT_TYPE[my_capacity]; //You missed this
}
In addition to missing your allocation in your current implementation, you are also dynamically allocating memory. A simple array type does not need to be allocated on the heap. The std::array collection does exactly what you are looking to do. I would urge you to look at its implementation for an example (if this is just an academic exercise). If this is for a production codebase, use what is already written and tested.
http://en.cppreference.com/w/cpp/container/array
Related
I have class XOBoard that present an array that is size n*n,each cell of the array is an Object called Cell.
Each Cell object is defined by
class Cell {
private:
char ch;
public:
Cell(char ch = '.');
char getCellValue();
void setCellValue(char nch);
};
Board is defined this way:
class XOBoard {
private:
int n;
Cell **Board;
};
XOBoard::XOBoard(int n) { //constructor
this->n = (n >= 3) ? n : 3;
Board = new Cell*[n];
for (int i = 0; i < n; i++) {
Board[i] = new Cell[n];
}
}
I wanted to get to a specific Cell value by using this method: board1[{1,2}], but I want to check if the values that sent to me is withing the range(n), but unfortantly I was unable to get to the Board array, and to n variable.
Here is the code:
XOBoard& operator[](list<int> list){
int x = list.front(), y = list.back();
return Board[x][y].getCellValue();
}
Thanks a head!
As mentioned in the comments, using operator[] for multidimensional subscripting is unconventional, but if you want that, you should make sure you get the correct amount of values (2 in this case) and that you return the correct type (a Cell& in this case).
Also be aware of shadowing. If you try to construct a Board with a value less than 3, you'll set this->n to 3 but go on with the construction using the erroneous n (that may even be a negative value).
More comments inline:
#include <iostream>
#include <stdexcept>
#include <tuple>
class Cell {
private:
char ch;
public:
Cell(char nch = '.') : // after the colon comes the member initializer list
ch(nch) // which is usually good to use
{
// if(ch is not valid) throw ...
}
char getCellValue() const { return ch; }
// a convenient conversion operator to automatically
// convert a Cell to a char where a char is needed
// (like when streaming a Cell to std::cout)
operator char() const { return ch; }
// void setCellValue(char nch); // replaced by operator=
Cell& operator=(char nch) {
// if(nch is not valid) throw ...
ch = nch;
return *this;
}
};
class XOBoard {
private:
size_t n; // use an unsigned type for sizes/indices
Cell** Board;
public:
// constructor
XOBoard(size_t xy_size) : // a member initializer list again
n(xy_size >= 3 ? xy_size : 3), // assign to "n" here
Board(new Cell*[n]) // the correct n is now used
{
// if the below construction fails, a bad_alloc will be thrown.
// you need to add code to clean up what you've already allocated to take
// care of that situation.
for(size_t i = 0; i < n; i++) {
Board[i] = new Cell[n];
}
}
// Copying or moving need careful handling of the pointers.
// Read "The rule of three/five/zero". Until then, disable it.
XOBoard(const XOBoard&) = delete;
XOBoard& operator=(const XOBoard&) = delete;
// destructor
~XOBoard() {
for(size_t i = 0; i < n; i++) delete[] Board[i];
delete[] Board;
}
// added for convenience
size_t size() const { return n; }
// return a Cell& and use a std::pair since you
// expect exactly 2 values
Cell& operator[](std::pair<size_t, size_t> pos) {
auto& [x, y] = pos;
if(x>=n || y>=n)
throw std::out_of_range("{"+std::to_string(x)+","+std::to_string(y)+"}");
return Board[x][y];
}
};
int main() {
try {
XOBoard a{2}; // trying an invalid size
std::cout << a.size() << '\n';
a[{2, 2}] = 'a';
std::cout << a[{2, 2}] << '\n';
Cell x = 'b';
a[{2, 2}] = x;
std::cout << a[{2, 2}] << '\n';
a[{2, 3}] = 'c'; // index out of bounds
} catch(const std::out_of_range& ex) {
std::cerr << "out_of_range exception: " << ex.what() << '\n';
}
}
Output:
3
a
b
out_of_range exception: {2,3}
You should try to avoid raw pointers and actual multidimensional arrays. It's often better to emulate dimensionality by allocating a 1d array and provide an interface to the user that calculates the correct element to work on.
I've read many posts with the same error, unfortunately all of those deal with indexing off the end of an array. In my case I get the error when I assign the array to a variable in my constructor.
Here is my code:
Heap.cpp
#include "./Heap.h"
#include <iostream>
#include <sstream>
// Provides floor, ceil, etc.
#include <cmath>
using namespace std;
Heap::Heap() {
arraySize = 0;
n = 0;
A = NULL;
}
// This assumes that every element of the array is an
// element of the heap.
Heap::Heap(int* inArray, int inArraySize, int inHeapSize) {
// TODO: initialize your class data members. An array dynamically allocated
// as follows:
// A = new int[size];
// If you allocate an array like this you MUST deallocate it in your
// destructor. This is done for you in the destructor below.
arraySize = inArraySize;
n = inHeapSize;
A = new int[arraySize];
A = inArray;
}
// Destructor. Cleans up memory.
Heap::~Heap() {
delete [] A;
}
// Note: the function name is prefixed by Heap:: (the class
// name followed by two colons). Any function defined in
// the .cpp file must have this prefix.
int Heap::at(int i) const {
return A[i];
}
int Heap::parent(int i) const{
return (int) (i - 1) / 2;
}
int Heap::left(int i) const {
return (i + 1)* 2 - 1;
}
int Heap::right(int i) const {
return (i + 1) * 2;
}
bool Heap::hasLeft(int i) const {
int leftIndex = left(i);
std::cout << "left index = " << leftIndex<< std::endl;
return false;
}
bool Heap::hasRight(int i) const{
return false;
}
void Heap::maxHeapify(int i){
}
//
void Heap::buildMaxHeap(){
}
bool Heap::operator==(const Heap& rhs) {
if (n != rhs.n) return false;
for (int i = 0; i < n; ++i) {
if (A[i] != rhs.A[i]) return false;
}
return true;
}
bool Heap::operator==(const int* rhs) {
for (int i = 0; i < n; ++i) {
if (A[i] != rhs[i]) return false;
}
return true;
}
std::ostream& operator<<(std::ostream& out, const Heap& h) {
out << "[";
for (int i = 0; i < h.n; ++i) {
out << h.A[i];
if (i < h.n-1) {
out << ", ";
}
}
out << "]";
return out;
}
string toDotImpl(const Heap& h, int i) {
using namespace std;
stringstream ss;
if (h.hasLeft(i)) {
ss << toDotImpl(h, h.left(i));
ss << "\"" << h.at(i) << "\" -> \""
<< h.at(h.left(i)) << "\"\n";
}
if (h.hasRight(i)) {
ss << toDotImpl(h, h.right(i));
ss << "\"" << h.at(i) << "\" -> \""
<< h.at(h.right(i)) << "\"\n";
}
return ss.str();
}
string toDot(const Heap& h) {
using namespace std;
stringstream ss;
ss << "digraph G {\n";
ss << "graph [ordering=\"out\"]\n";
ss << "\"" << h.at(0) << "\"\n";
ss << toDotImpl(h, 0);
ss << "}\n";
return ss.str();
}
and
Heap.h
#pragma once
// Provides I/O
#include <iostream>
// Provides size_t
#include <cstdlib>
// Provides INT_MAX and INT_MIN
// You can consider INT_MIN to be negative infinity
// and INT_MAX to be infinity
#include <climits>
//------------------------------------------------------------
// Heap class
//------------------------------------------------------------
class Heap {
public:
// Constructor
Heap();
// This constructor assumes that every element of the array is an
// element of the heap.
Heap(int* inArray, int inArraySize, int inHeapSize);
// Destructor
~Heap();
// Accesses an element of the array.
int at(int i) const;
// Gets parent index of element at i
int parent(int i) const;
// Return element to the left of i
int left(int i) const;
// Return element to the right of i
int right(int i) const;
// Checks if an element has a left child
bool hasLeft(int i) const;
// Checks if an elemnt has a right child
bool hasRight(int i) const;
// "Max heapifies" an array
void maxHeapify(int i);
// builds a max heap
void buildMaxHeap();
// Allows comparison between results
bool operator==(const Heap& rhs);
bool operator==(const int* rhs);
// Useful for debugging. To use:
// Heap h;
// cout << h << endl;
friend std::ostream& operator<<(std::ostream& out, const Heap& h);
private:
// The array
int* A;
// Size of the array
int arraySize;
// The number of elements in the heap
int n;
};
// Useful for debugging. To use:
// Heap h;
// cout << h << endl;
std::string toDot(const Heap& h);
The code is called with I can include the entire main.cpp if needed but it has several hundred lines of just test cases that are commented out.
int A[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
Heap h(A, 8, 8);
if I comment out A = inArray; the program runs so I'm pretty confident that is where the issue is.
A is defined in Heap.h as `int* A;
Here is the full error:
*** Error in `./project': free(): invalid size: 0x00007ffd84786660 ***
Aborted (core dumped)
this is probably quite a simple issue, but I can't figure out what is causing this since I believe this should allocate an array of size inArraySize of type int and then assign the given array inArray to A.
Full disclosure: this is part of an assignment so feel free to just point me in the right direction, but my professor is fine with us using stackoverflow as long as we site it.
You're trying to copy an array, but assigning pointers like that is not how to do it. There are various ways.
Standard C++:
#include <algorithm>
std::copy(inArray, inArray + inArraySize, A);
Using standard containers:
#include <vector>
std::vector<int> A(inArray, inArray + inArraySize);
Old style C way
memcpy(A, inArray, sizeof(int) * inArraySize);
Doing:
A = new int[arraySize];
A = inArray;
Is like doing:
i = 5;
i = 6;
The second assignment overrides the first one.
Hence as a result, the member variable A is pointing to the same memory block pointed by the input argument inArray.
If you haven't dynamically allocated this memory block (with new), then you cannot dynamically deallocate it (with delete).
The lines
A = new int[arraySize];
A = inArray;
are cause of two problems.
There is a memory leak. The value returned by new int[arraySize] is lost and cannot be deallocated.
If you are calling delete [] A in the destructor, that would be cause of the second problem.
If inArray was dynamically allocated and deallocated in the calling function, you will be calling delete on the same pointer twice.
If inArray was an array created in the stack, calling delete on it is also a problem. delete can be called only on memory that was returned by call to new.
A = inArray; is not doing what you think it is doing. This line does not copy inArray into the memory you allocated for A. Instead it changes A to point to a new location (the address of inArray), causing the previously allocated memory to leak. Later on when you call delete on A you'll be trying to free memory at inArray's address.
If you just want to copy an array, you could do something like
A = new int[inArraySize];
for (i = 0; i < inArraySize; ++i)
A[i] = inArray[i];
Or better yet, with std::copy:
std::copy(inArray, inArray + inArraySize, A);
I have a (what should be simple) assignment in my C++ class.
Here is the assignment:
Create a class template that contains two private data members: T * array and int size. The class uses a constructor to allocate the array based on the size entered. There is member function that allows the user to fill in the array based on the size. In addition, there is a member function that sorts the array and displays the sorted elements. Use the destructor to delete the array. Develop the main( ) to create two objects to call member functions. As a result, the first object will hold its array of double type, and the other will hold its array of the int type.
Here is what I have came up with but coming up with an error of "Allocation of incomplete type 'T'":
#include <iostream>
#include <new>
#include <vector>
using namespace std;
template <class T>
class DynArray {
protected:
int size;
T ** DynamicArray = new T[size];
public:
DynArray(){
void CreateArray(){
cout << "Enter size of Array: ";
cin >> size;
for (int i = 0; i < size; ++i){
DynamicArray[i] = new T();
}
for (int i = 0; i<size; i++) {
cout << "Element " << i << ": ";
cin >> DynamicArray[i];}
}
//Sort Array
void Sort(T a[], int size)
{
int idx, pass;
for (pass=0; pass<size; ++pass){
for (idx=0; idx<size-1; ++idx){
if (a[idx] > a[idx+1])
swap(a[idx], a[idx+1]);}
}
for (int i=0; i<size; ++i) {
for (idx = 0; idx<size-1; ++idx) {
cout << a[idx] << " ";
}
}
}
void DeleteArray(){
for (int i = 0; i < size; ++i){
delete DynamicArray[i];
}
delete[] DynamicArray;
}
};
int main() {
DynArray<class T>();
return 0;
}
Not sure if I am a complete retard in my line of thinking or if I am just missing a small element. Any help is great.
Thank you #jblixr and #user3655463 for your help. I got it worked out I think after your hints and help.
Here is what I came up with for reference if anyone else is working on this.
#include <iostream>
#include <new>
#include <algorithm>
using namespace std;
//Template Class T
template <class T>
class DynArray {
protected:
int size;
T * DynamicArray;
public:
DynArray(){};
DynArray(size_t s): size(s) {
DynamicArray = new T[size];
for (int i = 0; i<size; i++) {
cout << "Element " << i << ": ";
cin >> DynamicArray[i];
}
}
//Sort Array
void Sort(){
sort(DynamicArray, DynamicArray+size);
for (int i=0; i<size; i++) {
cout << DynamicArray[i] << endl;
}
}
//Clear Heap
~DynArray() {
delete []DynamicArray;
}
};
int main() {
int sizeOfArry;
cout << "Enter size of Array: ";
cin >> sizeOfArry;
//Use as an int Array;
DynArray<int> intArray = DynArray<int>(sizeOfArry);
intArray.Sort();
}
From description you have provided looks to me you want to do sth like that:
#include <iostream>
#include <algorithm>
using namespace std;
template <class T>
class DynArray {
protected:
int size;
T * DynamicArray;
public:
DynArray(size_t s): size(s) {
DynamicArray = new T[s];
}
void CreateArray(){
size_t size;
cout << "Enter size of Array: ";
cin >> size;
if(size > this->size)
size = this->size;
for (int i = 0; i<size; i++) {
cout << "Element " << i << ": ";
cin >> DynamicArray[i];
}
}
//Sort Array
void Sort()
{
std::sort(DynamicArray, DynamicArray+size);
}
DynArray() {
delete []DynamicArray;
}
};
int main() {
DynArray<double> dob(3);
DynArray<int> di(3);
dob.CreateArray();
di.CreateArray();
dob.Sort(); di.Sort();
}
I am wondering that whether you are creating a one dimensional array or 2D array, your initial requirements states one dimensional array but you are using 2D array in your code. Anyway, I play along with one dimensional array.
error: allocation of incomplete type 'T'
T *DynamicArray = new T[size];
What you are trying to do here is in-class initialization which can be done for static data members and it is an c++11 extension for non-static data member. So I suggest you not to do so since you are learning. You can only declare the members and don't initialize here.
Even if you make static it cannot be allocated because the template type T is known only after object creation, So the compiler isn't aware of the type it is going to allocate during the compile time.
So it should be simply as
T *DynamicArray;
Nested functions
C++ doesn't support nested functions, learn C++ syntax.
Usage of constructors and destructors
A constructor will do the functionality of CreateArray() and the destructor will do the functionlaity of DeleteArray()
Instantiating a template class
You should explicitly mention the type within the angle brackets that the template class is going to use
DynArray<int> intArray; // if you want to use int
DynArray<float> floatArray;
You can also use your own custom class for type T, hope you will learn soon in your class.
DynArray<MyCustomClass> customArray;
If you correct all these things then your final skeleton will be like the below one
template <class T>
class DynArray {
protected:
int size;
T *DynamicArray ;
public:
DynArray() {
// initailize DynamicArray here
// use your CreateArray() code here
}
void sort() {
// your own sort logic
// No need to pass the size to this function, its a member function
}
~DynArray() {
// use your DeleteArray() code here
}
};
int main() {
DynArray<int> intArray;
intArray.sort()
return 0;
}
Simple, isn't it? :)
I have a problem when accessing the overflowed square bracket in a public method of my class. Here is the code:
#include <iostream>
#include <cassert>
#include <cmath>
using namespace std;
template<unsigned int DIM> class Vector
{
private:
double mData[DIM];
public:
Vector(double tableau[DIM])
{
for(int i=0; i<DIM; i++)
{
mData[i] = tableau[i];
}
}
double operator[](int index)
{
assert(index < DIM);
assert(index > -1);
assert(-pow(10,-6)<=mData[index]<=1+pow(10,-6));
if(mData[index]>=0 && mData[index]<=1)
{
return mData[index];
}
else if(mData[index]<0 && mData[index]>=pow(10,-6))
{
return 0.0;
}
else if(mData[index]>1 && mData[index]<= 1+ pow(10,-6))
{
return 1.0;
}
}
double getDim() const
{
return DIM;
}
void print() const
{
for(int i=0;i<getDim();i++)
{
cout << this[i] << " "; //PROBLEM!!
}
}
};
int main()
{
double err=pow(10,-6);
double tableau[5];
tableau[0] = 0.5;
tableau[1] = 0.79;
tableau[2] = err;
tableau[3] = 1+err;
tableau[4] = 0;
Vector<5> proba(tableau);
proba.print();
}
I have tried with *this, this->, but anything appeared to work.
I hope you could help me.
Florent
Member operator overloads require a value or reference of the class type, and this is a pointer. So you either need to dereference the this pointer prior to using the operator, like this:
(*this)[i]
Or you can call the operator directly, which has the advantage of being totally explicit in its intent, but has the disadvantage of being a bit wordy and a bit more obscure (and therefore more likely to trip up anyone who reads it):
this->operator[](i)
If you have already tried *this[i] and found that it doesn't fix the problem, that's because it actually means *(this[i])!
Apart from wrong implementation of operator[], there is wrong use of it:-
cout << this[i] << " ";
This should be
cout << (*this)[i] << " "; //is you want to implement that way...
this is just a pointer, so to access operator[] you can either dereference it first:
cout << (*this)[i] << " ";
Or call the function directly (not preferred):
cout << this->operator[](i) << " ";
I am currently exploring the new c++11 features, I am wondering how to initialize the memory pointed by the unique_ptr, with the value I provided in the initializer_list. With a raw memory pointer like "T* data", I can use uninitialized_copy, but when I change from "T* data" to "unique_ptr data" (so I don't have to do explicit deconstruction) it does not work. I am still reading materials on unique_ptr (and my guess is that's because unique_ptr is not copyable), but could someone point out the function that does the job? Thanks!
#include <iostream>
#include <memory>
using namespace std;
template<class T>
class Vector
{
private:
unique_ptr<T []> data;
int size;
public:
Vector(initializer_list<T> l)
{
size = l.size();
cout << "Constructing, size is: " << size << endl;
data.reset(new T[size]); // reserve memory
uninitialized_copy(l.begin(),l.end(),data); // not working
//for(int i = 0; i<size; i++ ) // not working -> l has no operator[]
// data[i] = l[i];
}
void Report()
{
for(int i = 0; i<size; i++ )
cout << data[i] << ' ';
cout << '\n';
}
};
int main(int argc, const char *argv[])
{
int a = 1;
Vector<int> v {1,2,3,4};
v.Report();
return 0;
}
Simply use the getter :
std::copy(l.begin(),l.end(),data.get());