I would like to write a class template that is used to contain sequences of elements. I am working on a method that will behave like push_back() in vector. This is what i have done so far:
template <class T, int N>
class mysequence{
T memblock[N];
public:
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
memblock[y] = value;
}
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
And it returns:
-858993460
with error message. As far as i know the sizeof an empty class or structure is 1. But i also tried:
if (sizeof(memblock) == NULL) memblock[0] = value;
But all the same. I can not figure out what`s wrong with my code. If anyone has any ideas, i will apressiate it.
What is wrong is your use of sizeof. It does not tell you how many elements do you have, but just the memory used (N * sizeof(T)). To keep track of elements contained you seed a separate counter.
You are asking getmember() to return the first element of the array, but setmember() never fills in that element unless T is char, unsigned char, or bool, and N is 1. In any other combination, setmember() is storing the value past the end of the array. If you want to store a value in the last element, use N-1 instead (no need to calculate y as it will match N):
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
memblock[N-1] = value;
}
But, that is no use in a multi-element array. Since you want to mimic push_back(), you need to have setmember() store the value in the last available element instead:
template <class T, int N>
class mysequence{
T memblock[N];
int count;
public:
mysequence() : count(0) {}
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (count < N) {
memblock[count-1] = value;
++count;
}
else
throw length_error("sequence is full");
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
if ((x < 0) || (x >= count))
throw out_of_range("x is out of range");
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
Or, you could simply get rid of your array and use a real vector instead:
template <class T>
class mysequence{
vector<T> memblock;
public:
void setmember(T value);
T getmember(int x);
};
template <class T>
void mysequence<T>::setmember(T value) {
memblock.push_back(value);
}
template <class T>
T mysequence<T>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
This should indicate the problem:
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
cout << "y: " << y << endl;
memblock[y] = value;
}
}
When called with:
myints.setmember(1);
myints.setmember(5);
myints.setmember(8);
I get the output:
y: 14
y: 14
y: 14
y is always the same because it's calculated by finding the length of the underlying storage array, not the number of elements actually stored in that array.
Given that we aren't filling in the elements of the array correctly the values we get when we call getmember will be pulling out uninitialized elements of the array. This is where you get the garbage values such as -858993460 from (note that on my machine I got a different value here which is in line with the UB we have here).
If you want to support a push_back type of method that doesn't require an index to be passed in with the value then you need to change your design to have a field that keeps track of how many elements you have inserted into the array so far.
Additionally I'd make some checks to see if there is enough space to actually put the newest element into the back of the array before doing it.
You have misunderstood the functionality of push_back. What push_back does is that it adds elements to the end of an array, even an empty array. So I have modified some functionality of your functions and indicated them in the comments.
#include <iostream>
#include <algorithm>
using namespace std;
template <class T, int N>
class mysequence{
T *memblock;//You will need a pointer for enabling the resizing of the array
int size;//This is used to keep track of the size of the array
public:
mysequence();
mysequence(const mysequence &src);
~mysequence();
void setmember(T value);
T getmember(int x);
void push_back(T value);
mysequence& operator=(const mysequence &src);
};
template <class T, int N>
void mysequence<T, N>::mysequence(){
size = N;
memblock = new T[N];
}
template <class T, int N>
void mysequence<T, N>::mysequence(const mysequence<T,N> &src){
size = src.size
memblock = new T[size];
copy(src.memblock, src.memblock+size, memblock);
}
template <class T, int N>
void mysequence<T, N>::~mysequence(){
delete[] memblock;
}
template <class T, int N>
void mysequence<T, N>::setmember(T value) {//this setmember function just sets the last element of the array to the value specified
if( size > 0 ){
memblock[size-1] = value;
}
}
template<class T, int N>
void mysequence<T,N>::push_back(T value){
T *pointer = new T[size+1];
copy(memblock, memblock+size, pointer);
pointer[size] = value;
delete[] arr;
arr = pointer;
size++;
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
template<class T, int N>
mysequence<T,N>& mysequence<T,N>::operator=(const mysequence<T,N> &src){
T *pointer = new T[src.size];
copy(src.memblock, src.memblock+src.size, pointer);
delete[] arr;
arr = pointer;
size = src.size;
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(13)<<endl;//the indexing is one less. so to access the last element, you need to specify 13. As other values are not initialized, they will have garbage value in them
myints.push_back(1);
cout<<myints.getmember(14)<<endl;
return 0;
}
Related
I'm trying to make a simple template of creating an array class. However, I want to use two arguments: one typename and one int variable. I'm running into two errors and I'm not sure what I'm doing wrong. Here is my source which is simple and works.
int main()
{
Array<int, 7> tArray;
std::cout << tArray.getSize() << std::endl;
for (int i = 0; i < tArray.getSize(); i++)
{
tArray.insert(i + 5);
i++;
}
tArray.printArray();
return 0;
}
The error/issue I'm having is regarding two functions in my header file.
#pragma once
template <typename T, int S>
class Array
{
private:
T arr[S];
public:
Array();
int getSize() const;
int insert(T val);
void printArray();
~Array();
};
cArray<T, S>::cArray() { }
template <typename T, int S>
int Array<T, S>::insert(T val)
{
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
template <typename T, int S>
void Array<T, S>::printArray()
{
Array<T, S>* temp = new Array[S];
temp = this;
for (int i = 0; i < S; i++)
{
std::cout << tempArray[i] << std::endl;
}
}
The functions insert and printArray are not working. For the insert function, the error being returned states: Suppression State Error C2440 'return': cannot convert from 'Array<int,7>' to 'int'
The printArray is displaying nothing.
This is the first time I'm using a template with two arguments so I'm a bit confused on what I'm doing wrong here. Any takes?
The offending code is here:
template <typename T, int S>
int Array<T, S>::insert(T val) {
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
First, you create an array of Arrays, because you wrote an expression that looks like:
Type *var = new Type[count];
Then you change the pointer to that array to this. Then you try to return the element at index val of the array temp, and this element has type Array<T, S>. But, the return type is int, so, that is why the compiler complains that it cannot convert Array<int, 7> to int.
You should not use new here at all. You already have allocated memory for the array (it's the member variable arr), you just need to copy val into the right place in that array. To do that, you need to keep track of how many elements you already inserted in the array. (Note that S is a constant that just says what the maximum amount of elements is you can store.) So:
template <typename T, int S>
class Array {
T arr[S];
size_t size = 0;
...
};
template <typename T, int S>
void Array<T, S>::insert(T val)
{
arr[size++] = val;
}
template <typename T, int S>
size_t Array<T, S>::getSize()
{
return size;
}
And printArray() has similar issues. You should print the contents of arr, you should not create a new temporary array and try to print its elements.
For a school assignment I want to build a custom array container as I'm not allowed to use containers provided by std, or any library available, while self made is allowed.
So far everything I have is working but I want to double my array size as soon as I reach the limit. how can i do this, all i can find is using vector (which i'm not allowed).
#ifndef UTILS_ARRAY_HEADER_INCLUDED
#define UTILS_ARRAY_HEADER_INCLUDED
#include <array>
namespace utils
{
template <class T>
struct Array
{
private:
int count = 0;
int size = 1;
std::array<T, 1> myArray;
void doubleSize();
public:
T* begin();
T* end();
T& operator[] (int);
void addItem(T const);
};
template <class T>
T* Array<T>::begin()
{
return &myArray[0];
}
template <class T>
T* Array<T>::end()
{
if (&myArray[count])
return &myArray[count];
return &myArray[0];
}
template <class T>
T& Array<T>::operator[] (int key)
{
return myArray[key];
}
template <class T>
void Array<T>::addItem(T const item)
{
if (count >= 0 && count < size)
{
myArray[count] = item;
count++;
}
else {
doubleSize();
}
return;
}
template <class T>
void Array<T>::doubleSize()
{
// ?
/*size = size * 2;
const int newsize = 2;
std::array<T, newsize> newArray; // not working.
std::copy(std::begin(myArray), std::end(myArray), std::begin(newArray));
myArray = newArray;*/
}
}
#endif
You need properties:
current capacity: int
current size (max used index): int
pointer to your data: T *
In AddItem check if current_size < current_capacity. If yes,
create new_data with size of currernt_capacity * 2, copy each item, delete old data and replace pointer.
Remember to do delete data; in destructor. I won't give you more code, it's your homework.
Checkout valgrind to check if your code does not leak memory.
I suspect that std::array<T, 1> is not allowed either. But that's not a real problem: you can't use array anyway since it has a fixed size.
You'll need to store a T* begin, a size_t current_size and a size_t size_in_use.
I call a function like this:
void do_work(void* ptr, size_t size)
{
//do things with ptr and size
}
The question is: is it possible to call the function like do_work(one); and somehow get the sizeof inside the function?
Given one is a static array (int one[10];) or non array (int one;), is it possible now?
In C++ you could use template argumment deduction to get the type of the pointer (the pointed type) and then compute its sizeof:
template<typename T>
void do_work( T* ptr )
{
constexpr std::size_t size = sizeof(T);
}
You can use a function template:
template <typename T>
void do_work(T* stuff)
{
const size_t s = sizeof(T);
....
}
Edit: version for arrays:
template<typename T, size_t N >
void do_work(T (&array)[N] )
{
const size_t s = N * sizeof(T);
}
No, not for void *, you will not be able to compute the size of the object whose address is passed.
template <typename T>
void do_work(T * t) { do_work(t, sizeof(T)); }
template<typename T>
void do_work(T* ptr)
{
size_t size = sizeof(T);
size = sizeof(*ptr); // or this
}
I don't know C++, this question appeared before me, probably because it previously had the C tag. But here's something that you could do in C, probably also in C++:
#define do_work(_a_) do_work_func(_a_, sizeof(_a_))
void do_work_func( void* ptr, size_t size )
{
//do things with ptr and size
}
Which would work out, given that one in do_work( one ); call had been defined as an array.
If I get you right you might want:
#include <iostream>
void do_work(void* ptr, size_t size)
{
std::cout << "Size: " << size << '\n';
}
template <typename T>
void do_work(T& single) {
do_work(&single, 1);
}
template <typename T, std::size_t N>
void do_work(T (&array)[N]) {
do_work(array, N);
}
template <typename T>
void do_work(T* multiple, std::size_t n) {
do_work(static_cast<void*>(multiple), n);
}
int main()
{
int a = 1;
int b[] = { 1, 2, 3 };
int* c = b;
do_work(a);
do_work(b);
do_work(c, 3);
}
Note: Why the void* ?
In the following:
int c[10] = {1,2,3,4,5,6,7,8,9,0};
printArray(c, 10);
template< typename T >
void printArray(const T * const array, int count)
{
for(int i=0; i< count; i++)
cout << array[i] << " ";
}
I am a little confused why the function signature of the template function makes no reference to array being an array by using [], so something like const T * const[] array.
How could one tell from the template function signature that an array is being passed and not just a non-array variable??
You cannot tell for sure. You would have to read the documentation and/or figure it out from the names of the function parameters. But since you are dealing with fixed sized arrays, you could have coded it like this:
#include <cstddef> // for std::size_t
template< typename T, std::size_t N >
void printArray(const T (&array)[N])
{
for(std::size_t i=0; i< N; i++)
cout << array[i] << " ";
}
int main()
{
int c[] = {1,2,3,4,5,6,7,8,9,0}; // size is inferred from initializer list
printArray(c);
}
An array has a size. To create a reference to an array, you need to provide the size statically. For example:
template <typename T, std::size_t Size>
void printArray(T const (&array)[Size]) {
...
}
This functions takes the array by reference and you can determine its size.
You could try something like the following:
template< std::size_t N>
struct ArrayType
{
typedef int IntType[N];
};
ArrayType<10>::IntType content = {1,2,3,4,5,6,7,8,9,0};
template< std::size_t N >
void printArray(const typename ArrayType<N>::IntType & array)
{
//for from 0 to N with an array
}
void printArray(const int * & array)
{
//not an array
}
Raxvan.
I'm trying to implement a hypercubeclass, that is, multidimensional vectors.
I have a problem generalizing it. I'm able to make one for a three dimensional hypercube, but as mentioned, the problem is generalizing it. Could anyone help me? You should be able to write hypercube<4> w(5) to get 4 dimensions and 5 elements in each vector that is 5*5*5*5 elements in total.
Here is the code I have for the three dimensional version:
#include <vector>
using std::vector;
using namespace std;
template <int b>
class Hypercube {
public:
Hypercube(int a) : intvec(a){
for (int i = 0; i<a;i++) {
intvec[i].resize(a);
for (int j = 0;j<a;j++) {
intvec[i][j].resize(a);
}
}
}
vector<vector<int> >& operator[](int i) {
return intvec[i];
}
vector<vector<vector<int> > > intvec;
};
For this to work, you need recursive inheritence to provide the correct vector type and the initialization function. Both work recursively, for which I created a little helper struct called hcube_info:
// hypercube.h
#include <vector>
template<unsigned N>
struct hcube_info;
template<>
struct hcube_info<1>
{ // base version
typedef std::vector<int> type;
static type init(unsigned innerdim, int value = 0){
return type(innerdim, value);
}
};
template<unsigned N>
struct hcube_info
{ // recursive definition, N dimensions
private:
typedef hcube_info<N-1> base;
typedef typename base::type btype;
public:
typedef std::vector<btype> type;
static type init(unsigned innerdim, int value = 0){
return type(innerdim, base::init(innerdim, value));
}
};
As you can see, recursion all the way to the one dimensional base case. We also need to recursively initialize the vector to pass the inner dimension all the way through.
And now the real class, a nice interface around the hcube_info:
template<unsigned N>
struct hypercube
{
private:
typedef hcube_info<N> info;
typedef typename info::type vec_type;
public:
typedef typename vec_type::value_type value_type;
typedef typename vec_type::size_type size_type;
explicit hypercube(unsigned innerdim, unsigned value = 0)
: c(info::init(innerdim, value))
{
}
value_type& operator[](unsigned i){
return c[i];
}
size_type size() const{ return c.size(); }
private:
vec_type c;
};
Test program:
#include "hypercube.h"
#include <iostream>
int main(){
hypercube<4> c(5);
unsigned s = c.size() * // dim 1
c[0].size() * // dim 2
c[0][0].size() * // dim 3
c[0][0][0].size(); // dim 4
std::cout << s << '\n'; // outputs: 625 -> 5 * 5 * 5 * 5 -> 5^4
}
I would suggest something along those lines:
template <typename T, unsigned dim> class HQ {
std::vector<HQ<T,(dim-1)> > vector;
public:
HQ(unsigned size) : vector(size,HQ<T,(dim-1)>(size)) {}
};
template <typename T> class HQ<T,1> {
std::vector<T> vector;
public:
HQ(unsigned size) : vector(size,T()) {}
};
template <typename T> class HQ<T,0> {};
You can then implement your accessors for the first both templates as you wish. You can also make things a bit more simple and robust by allowing zero-dimensional matrices:
template <typename T, unsigned dim> class HQ {
std::vector<HQ<T,(dim-1)> > vector;
public:
HQ(unsigned size) : vector(size,HQ<T,(dim-1)>(size)) {}
};
template <typename T> class HQ<T,0> {
T data;
public:
HQ(unsigned size) : data() {}
};
I imagine an access operator would look something like this:
template <typename T, unsigned dim> HQ<T,(dim-1)>& HQ<T,dim>::operator[](unsigned i) {
return vector[i];
}
template <typename T, unsigned dim> HQ<T,(dim-1)> const& HQ<T,dim>::operator[](unsigned i) const {
return vector[i];
}
such that you can write
HQ<int,4> hq(5);
hq[1][4][2][0] = 77;