I'm honestly stuck on how to asssign an array's size during the call of the constructor. Also, I would like the array to be a 'const'. Is this possible during the constructor? Or do I have to do some more tricky stuff? Here is part of the code:
class CustomBitmap
{
public:
CustomBitmap(int width,int height);
~CustomBitmap(void);
private:
const int m_width;
const int m_height;
char const m_components[];
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "CustomBitmap.h"
CustomBitmap::CustomBitmap(int width,int height) : m_width(width), m_height(height)
// How do I implement the array? none of the syntax works, I tried m_components([width * height *4]) and all sorts of things along that line.
{}
CustomBitmap::~CustomBitmap(void) {}
An array has a fixed size (at least in standard C++), so you cannot assign a size to it at runtime, but have to specify its size at compile time.
If you want a variable size, use a std::vector, in your case
std::vector<char> m_components;
If the vector is const, then you won't be able to change/append to it, so I don't really see the point of making it const, unless you trivially initialize it in class (C++11) as e.g.
const std::vector<char> m_components(10, 'a'); // const char vector of 10 a's
or
const std::vector<char> m_components = {'a','b','c'}; // again C++11 in class initialization
You may also do something like
template<int N>
class CustomBitmap
{
...
char m_compontents[N];
}
but again, this is a template class for which you have to specify the template parameter N at compile time, i.e. instantiate it as e.g.
CustomBitmap<5> my_custom_bitmap; // now m_components has size 5
This kind of syntax:
m_components([width * height *4])
would be called variable length arrays. VLA. this is C99 norm.
You can't do it here because it exists within your type, which would make the type size itself variable, which is impossible because it is not template.
You can make it template with an integral parameter (rather than typename you use int) and then you can do what you want.
Howver, you should not do it, because your type will become enormous, which is a bad idea since automatically allocated types can be on the stack, which will explode the limit very quickly and cause a number of inefficiencies since compilers are not used to stack bloat.
You need to allocate on the heap, using std::vector is the best answer here. Also be aware for later that std::array<T,N> type exists to do what I described in the first paragraph.
Related
I am writing an implementation for a neural network, and I am passing in the number of nodes in each layer into the constructor. Here is my constructor:
class Network {
public:
template<size_t n>
Network(int inputNodes, int (&hiddenNodes)[n], int outputNodes);
};
I am wondering if it is bad practice to use templates to specify array size. Should I be doing something like this instead?
class Network {
public:
Network(int inputNodes, int numHiddenLayers, int* hiddenNodes, int outputNodes);
};
Templates are necessary when you want to write something that uses variable types. You don't need it when you just want to pass a value of a given type. So one argument against using a template for this is to keep things simple.
Another problem with the template approach is that you can only pass in a constant value for the size. You can't write:
size_t n;
std::cin >> n;
Network<n> network(...); // compile error
A third issue with the template approach is that the compiler will have to instantiate a specialization of the function for every possible size you are using. For small values of n, that might give some benefits, because the compiler could optimize each specialization better when it knows the exact value (for example, by unrolling loops), but for large values it will probably not be able to optimize it any better than if it didn't know the size. And having multiple specializations might mean the instruction cache in your CPU is trashed more easily, that your program's binary is larger and thus uses more disk space and memory.
So it likely is much better to pass the size as a variable, or instead of using a size and a pointer to an array, use a (reference to an) STL container, or if you can use C++20, consider using std::span.
Use std::span<int> or write your own.
struct int_span {
int* b = 0;
int* e = 0;
// iteration:
int* begin() const { return b; }
int* end() const { return e; }
// container-like access:
int& operator[](std::size_t i) const { return begin()[i]; }
std::size_t size() const { return end()-begin(); }
int* data() const { return begin(); }
// implicit constructors from various contiguous buffers:
template<std::size_t N>
int_span( int(&arr)[N] ):int_span( arr, N ) {}
template<std::size_t N>
int_span( std::array<int, N>& arr ):int_span( arr.data(), N ) {}
template<class A>
int_span( std::vector<int, A>& v ):int_span(v.data(), v.size()) {}
// From a pair of pointers, or pointer+length:
int_span( int* s, int* f ):b(s),e(f) {}
int_span( int* s, std::size_t len ):int_span(s, s+len) {}
// special member functions. Copy is enough:
int_span() = default;
// This is a view type; so assignment and copy is copying the selection,
// not the contents:
int_span(int_span const&) = default;
int_span& operator=(int_span const&) = default;
};
there we go; an int_span with represents a view into a contiguous buffer of ints of some size.
class Network {
public:
Network(int inputNodes, int_span hiddenNodes, int outputNodes);
};
From the way you write the second function argument
int (&hiddenNodes)[n]
I guess you're not an experienced C/C++ programmer. The point is that n will be ignored by the compiler and you'll lose any possibility to verify that the size of the C-style array you'll input here and the n passed as the template parameter will be equal to each other or at least coherent with each other.
So, forget about templates. Go std::vector<int>.
The only advantage of using a template (or std::array) here is that the compiler might optimize your code better than with std::vector. The chances that you'll be able to exploit it are, however, very small, and even if you succeed, the speedup most likely be hardly measureable.
The advantage of std::vector is that it is practically as fast and easy to use as std::array, but far more flexible (its size is adjustable at runtime). If you go std::array or templates and you are going to use in your program hidden layers of different sizes, soon you'll have to turn other parts of your program into templates and it is likely that rather than implementing your neural network, you'll find yourself fighting with templates. It's not worth it.
However, when you'll have a working implementation of your NN, based on std::vector, you can THEN consider its optimization, which may include std::array or templates. But I'm 99.999% sure you'll stay with std::vector.
I've never implemented a neural network, but did a lot of time-consuming simulations. The first choice is always std::vector and only if one has some special, well defined requirements for the data container does one use other containers.
Finally, keep in mind that std::array is stack-allocated, whereas std::vector is allocated on the heap. Heap is much larger and in some scenarios this is a crucial factor to consider.
EDIT
In short:
if an array size may vary freely, never pass its value as a
template parameter. Use std::vector
If it can take on 2, 3, 4, perhaps 5 sizes from a fixed set, you CAN consider std::array, but std::vector will most likely be as efficient and the code will be simpler
If the array will always be of the same size known at compile-time, and the limited size of the function stack is not an issue, use std::array.
I want to create a function that resizes a stack. It will create a new array with a larger or smaller size depending on the value of max, then copy the elements into that new array.
void resize(const int& max) {
std::array<Item, max> temp;
for (int i = 0; i < n; i++) {
temp.at(i) = a.at(i);
}
a = temp;
}
I know this will not run because max is not a constant expression. I absolutely don't know how to pass a constant int value. I tried:
void resize(constexpr int& max) //compiler says cannot make int arg constexpr
I don't want to do constexpr void resize because I don't need the function to be evaluated at compile time, and it didn't work anyway.
Note: I know this might be easier if I used std::vector because it's resizable, but I want to try to experiment with std::array.
What should I do?
I think you are mistaking compile-time constant and run-time constant. You cannot use std::array in this way. You want a container whose size is not compile-time constant, and that would be std::vector.
If you want your container's size to be constant throughout the runtime, you can either rely on self discipline (i.e : do not use push_back() or resize() etc..) Or you could write a wrapper class (.i.e a class that has-a std::vector as a member) and choose the methods of this class carefully to never resize the vector.
You can also use dynarray but it has been rejected from c++11 (and c++14 If I recall correctly)
PS: it is quite weird to name your method resize() and then say that you want constant-sized arrays =)
You do realize that if you call this method you are indeed changing the size of your array at runtime, in which case why not use std::vector and be done with it ?
I would like to know, if I have a class with an array attribute whose size is not the same for all instances :
class myObject{
private:
int size;
int* array;
// other methods/attributes
};
Is it obligatory allocated using new ?
explicit myObject(int size = 0):size(size){
array = new int[size];
}
Even if in the main(), I always use constant parameters to create the instances of the class ? (Meaning I know every array size at compile time).
int main{
myObject object (5);
return 0;
}
Apparently something like :
private:
int size;
int array[size];
wont work, no ?
That means that array attribute whose size are not constant of the class are obligatory on the heap ?
Thank you for your answers,
That class contains no array. What you called array is a pointer; you cannot store any ints in it. If you really do just store a pointer, you'll have to allocate the memory yourself somehow; it can't magically appear. You'll also have to deallocate it yourself, and make sure that copying and assigning myObject objects doesn't cause any issues.
However, it's unlikely that a pointer is really the best way to do things. The standard library provides the std::vector class template which lets you use almost exactly the syntax you want:
class myObject {
std::vector<int> vector;
public:
myObject() {};
explicit myObject(std::size_t n) : vector(n) {}
};
With this in place you can create myObjects and they'll have the right amount of storage ready for them. It'll likely be dynamically allocated using operator new[], just like if you'd do it manually, but you don't have to worry about copying or deleting it.
int main() {
myObject a; // default-constructed, vector is empty.
myObject b(10); // std::size_t constructor, vector has 10 elements.
} // scope exit, b and a destroyed.
You can use the vector member much like if it was an array; the only thing it does not support is implicit decay to pointer, but the data member function makes up for even that.
As an alternative, if you always know the size at compile-time you can change the class into a class template and make the size a template parameter:
template<std::size_t N>
class myObject{
std::array<int, N> array;
// other methods/attributes
};
However, note that you now cannot use myObject<10> to a function expecting myObject<20>.
It is unlikely that you want more control than the above possibilities provide -- std::vector can be given an allocator, so it can do almost all work for you -- you could use std::unique_ptr<int[]> and make_unique together to make things work for you. However, if you need this kind of power, you probably know it yourself.
As a closing note, if you're just learning C++ and your book doesn't cover std::vectors somewhere early on, perhaps it's best to get a different book; they're one of the most commonly-useful data structures in the standard library and definitely not something to be left in an appendix.
If you need a variable sized array as a member of a class, don't use built-in arrays directly. Instead, use std::vector<T>, e.g.:
class myObject {
std::vector<int> array;
public:
explicit myObject(int size = 0): array(size){}
};
You can get the std:vector<int>'s size using array.size(), i.e., there is no need to store the size separately. Also, the content is automatically default initialized.
It's possible to create an array like this - if size is defined before arr.
const int size = 5;
int arr[size];
But it's not possible, when size and arr are inside object or struct:
struct A{
A(int s) : size(s)
{}
const int size;
int arr[size]
};
A(5);
Why is it so? It's seems kinda illogical, because in both cases size of array is known in compilation time.
It's seems kinda illogical, because in both cases size of array is known in compilation time.
The compiler cannot distinguish between your example and this:
int i = std::rand(); // not a compile time constant
A a(i);
On the other hand, it needs to know the size of the array at compile time. Two instances of A cannot have different size. So the size of the array cannot depend on something that can be set at runtime.
On the other hand, C++11 provides constexpr, which allows you to propagate compile-time constants through expressions and lets you use these to initialize arrays.
in both cases size of array is known in compilation time.
Nope, a const class member can be set at run-time.
In fact, even a non-member doesn't always work:
int x;
cin >> x;
const int y = x;
int z[y]; //ILLEGAL
Just use a std::vector.
When the compiler compiles A, there is in fact no guarantee that it is a compile-time argument. It just happens to be one. I could equally do
int main() {
std::cin >> i;
A(i);
}
If you want to pass it a constexpr argument, you will need to use a template. Else, you will need to use a run-time container like std::vector.
You should use std::vector<int> instead of raw arrays if you want a dynamically resizeable array.
You cannot do what you're trying to do inside the class or struct because your size member has no defined value.
Because in C++ when you initialize a C-style array with a size argument, the size must be a compile-time integral constant.
Otherwise the compiler doesn't know how big to make the variable when compiling.
If you don't like this, you should be using vector anyway.
In second case, if you take into consideration just class definition array size is not known in compilation time.
if making such classes was allowed you could write
A(5);
A(6);
that would be objects of different size, that would break alot, for example it would not be possible to keep such objects in the same array. It would be logical to say that those objects have different type, and that would be possible if you make your class template and pass size as template parameter.
if you want to use arrays of different size use std::vector or templates.
You need to dynamicly allocate the memory from your heap if it is not known before compile time.
int* arr = new int [size];
If you do not need the memory anymore you should delete the array.
delete[] arr;
And also as Tony said, it is better to use standart libary vectors
I want to statically allocate the array. Look at the following code, this code is not correct but it will give you an idea what I want to do
class array
{
const int arraysize;
int array[arraysize];//i want to statically allocate the array as we can do it by non type parameters of templates
public:
array();
};
array::array():arraysize(10)
{
for(int i=0;i<10;i++)
array[i]=i;
}
main()
{
array object;
}
If your array size is always the same, make it a static member. Static members that are integral types can be initialized directly in the class definition, like so:
class array
{
static const int arraysize = 10;
int array[arraysize];
public:
array();
};
This should work the way you want. If arraysize is not always the same for every object of type array, then you cannot do this, and you will need to use template parameters, dynamically allocate the array, or use an STL container class (e.g. std::vector) instead.
It has to be done using template parameters, otherwise sizeof(array) would be different for every object.
This is how you would do it using template parameters.
template <int N>
class array
{
int data[N];
// ...
};
Or, you could use an std::vector if you don't mind dynamic allocation.
C++ doesn't allow variable-length arrays (i.e. ones whose sizes are not compile-time constants). Allowing one within a struct would make it impossible to calculate sizeof(array), as the size could differ from one instance to another.
Consider using std::vector instead, if the size is known only at runtime. This also avoids storing the array size in a separate variable. Notice that allocating from heap (e.g. by std::vector) also allows bigger arrays, as the available stack space is very limited.
If you want it a compile-time constant, take a template parameter. Then you should be looking for Boost.Array, which already implements it.
The array size must be a compile time constant. You are almost there, you just need to initialize the const and make it a static as well. Or, use a dynamic array or a vector.
EDIT: note about this answer: This is most likely the wrong way to do this for your situation. But if you really need it to be an array (not a vector or whatever) and you really need it to be dynamically allocated, do the following:
class array
{
int *p_array;
public:
array(int size);
};
array::array(int size)
{
p_array = malloc(size * sizeof(int));
}
Just make sure you clean up (IE free p_array in your descructor)