I was wondering if it is possible to declare an array (size not known at this time), as a private member of a class and later set the size in the constructor of the class. For example:
class Test {
int a[];
public:
Test(int size);
};
Test::Test(int size) {
a[size]; // this is wrong, but what can i do here?
}
Is this possible or should I use dynamic arrays? Thanks!
Short Answer: No (The size of an array is defined at compile time only)
Long Answer:
You can use a vector to achieve the same result:
class Test
{
std::vector<int> a;
public:
Test(std::size_t size):
a(size)
{}
};
No this is not possible. Array declarations in headers must have constant sized value. Otherwise it's impossible for constructs like "sizeof" to function properly. You'll need to declare the array as a pointer type and use new[] in the constructor. Example.
class Test {
int *a;
public:
Test(int size) {
a = new int[size];
}
~Test() { delete [] a; }
private:
Test(const Test& other);
Test& operator=(const Test& other);
};
As other answers have pointed out, the size of an array is fixed at compile time. However, by using templates you can parameterise the size at compile time:
template <int N> class Test {
int a[N];
public:
Test() { }
};
Test<5> test;
Test<40> biggertest;
This technique does not let you compute the size at run time (as the dynamic std::vector solution does), but depending on your needs this may be sufficient.
First of all, it is generally better to initialize things in the initialization list of the constructor, not in the body of the constructor.
You can only initialize an array with a predefined bound if you know that bound at compile time. In this situation, you will need to dynamically allocate the space.
You must remember then to have a destructor that would delete the array when the object is destroyed or you would get a memory leak.
See Martin's solution (use std::vector), and remember that even if you need to pass a buffer to a C API std::vector lets you do it by passing &vec[0] :
std::vector<char> vec(10);
memset(&vec[0], 0, vec.size());
It's guaranteed to work, but only if the vector isn't empty (C++ quirks, <sigh>).
No, this is not possible. You should use a dynamic array such as an std::vector. C99 allows a struct to have an unsized array as the last member only, but even when you do this you still have to manually allocate the memory yourself, e.g. with malloc().
What you're talking about is not possible. Classes always have a constant size. You could have your class use a pointer to a dynamically allocated array or you could use a std::vector.
Related
I need to use array as a class property but I am not sure about the correct way. Assume that I have a class called A and I need an integer array in it.
First, if I want a static array with 10 elements, is the following way correct? If not, how should it be?
class A {
public:
int arr[10];
};
Second, if I want a dynamic array, which one of the followings is correct? If neither of them, I would be appreciated if you explain the reason and correct way.
class A {
public:
int *arr;
};
class A {
public:
int arr[];
};
Finally, what happens if I initialize a property in class definition as following
class A {
public:
int arr[] = {1,2,3};
// or
int *arr = new int[5];
// or
int number = 5;
};
Thanks a lot.
Note: I am aware of that it is much better to use vector or that kind of STL data structure but I need to stick to arrays somehow.
First, if I want a static array with 10 elements, is the following way correct?
Yes, that is a correct way.
Some people prefer to wrap the array in a class template such as std::array.
Second, if I want a dynamic array, which one of the followings is correct? If neither of them, I would be appreciated if you explain the reason and correct way.
class A {
public:
int arr[];
};
That is an ill-formed declaration. A member cannot be an array of indeterminate length.
class A {
public:
int *arr;
};
This is correct as such, but there is no dynamic array created. Using this for owning a dynamic array is bad design because bare pointers should never have ownership.
Even for pointing an array stored elsewhere, this is problematic since the class has no knowledge of the length of the array.
This would be a correct way, assuming the array is owned by the class:
class A {
public:
std::vector<int> arr;
};
If the array isn't owned by the class, then following is correct:
class A {
public:
std::span<int> arr;
};
std::span isn't in the standard until C++20 though, so until then you need to resort to a non-standard implementation.
Finally, what happens if I initialize a property in class definition as following
class A {
public:
int arr[] = {1,2,3};
That is ill-formed because the size of a member cannot be deduced from the initaliser.
int *arr = new int[5];
new int[5] is a default member initialiser. It will be used in case no initialiser is provided for the member otherwise.
For fixed-size arrays you can use int arr[10] or std::array<int, 10>.
For dynamically-sized or resizeable arrays if you cannot use std::vector or std::unique_ptr<int[]> you should use int* (int[] is not a valid data member type in C++) and implement constructor, copy constructor, move constructor, copy assignment, move assignment and destructor. See rule of five for more details.
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
Let's say I have a class that has a member which is an array. Is it possible to define its size upon construction/at run-time, in the following way:
class myClass {
private:
int myArray[n]
public:
myClass();
someOtherMethod();
};
Where n is a variable that is defined based on user input. If not, what would be the best alternative?
It depends.
Semantically, there are 3 types of arrays:
arrays with a size fixed at compile time
arrays with a size fixed at runtime
arrays with a dynamic size
C++ directly supports the first and third cases, respectively with regular arrays and the std::vector class.
C also supports the second type with two constructs:
variable length arrays (on the stack)
the oldie struct hack or tail-padding
I would advise, in C++, using the std::vector class in your case. It provides more than what you need, but is simpler to use.
On the other hand, you can still use tail-padding, even in C++. It does require careful engineering though.
Use a vector.
class myClass {
private:
std::vector<int> myArray;
public:
myClass();
someOtherMethod();
};
myClass::myClass (int size)
: myArray (size)
{
...
}
Then, you can fill in the vector as you would an array. Alternatively, as Nawaz points out, use reserve(), which reserves space for new elements, and/or push_back(), which adds elements onto the back, one at a time.
The class template std::vector is designed for this purpose.
class myClass {
private:
std::vector<int> myArray;
public:
myClass(int size);
someOtherMethod();
};
myClass::myClass(int size) : myArray(size)
{
}
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)